@taskless-app/swarm 0.6.20 → 0.6.22
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 +82 -6
- package/dist/overmind.js +497 -76
- package/package.json +2 -2
- package/roles/drone/skills/swarm-verify-green/SKILL.md +33 -0
package/dist/overmind.js
CHANGED
|
@@ -1850,10 +1850,24 @@ function asTokenUsage(m) {
|
|
|
1850
1850
|
function budgetExhausted(result) {
|
|
1851
1851
|
return result.subtype === "error_max_budget_usd" || result.terminal_reason === "budget_exhausted";
|
|
1852
1852
|
}
|
|
1853
|
+
function sessionAbort(result) {
|
|
1854
|
+
if (budgetExhausted(result)) return null;
|
|
1855
|
+
const reason = result.terminal_reason ?? "";
|
|
1856
|
+
const subtype = result.subtype ?? "";
|
|
1857
|
+
if (reason === "aborted_tools") return { kind: "tools", raw: reason };
|
|
1858
|
+
if (reason === "aborted_streaming" || reason === "api_error") return { kind: "link", raw: reason };
|
|
1859
|
+
if (HONEST_ENDINGS.has(subtype) || HONEST_ENDINGS.has(reason)) return null;
|
|
1860
|
+
const failed = result.is_error === true || subtype === "error_during_execution" || reason.startsWith("aborted");
|
|
1861
|
+
if (!failed) return null;
|
|
1862
|
+
const raw = reason && reason !== "completed" ? reason : subtype || reason || "\u043E\u0448\u0438\u0431\u043A\u0430 \u0431\u0435\u0437 \u043C\u0435\u0442\u043A\u0438";
|
|
1863
|
+
return { kind: "other", raw };
|
|
1864
|
+
}
|
|
1865
|
+
var HONEST_ENDINGS;
|
|
1853
1866
|
var init_claudeResult = __esm({
|
|
1854
1867
|
"src/claudeResult.ts"() {
|
|
1855
1868
|
"use strict";
|
|
1856
1869
|
init_pricing();
|
|
1870
|
+
HONEST_ENDINGS = /* @__PURE__ */ new Set(["error_max_turns"]);
|
|
1857
1871
|
}
|
|
1858
1872
|
});
|
|
1859
1873
|
|
|
@@ -1928,6 +1942,9 @@ function writeMcpConfig(spec) {
|
|
|
1928
1942
|
writeFileSync2(path, JSON.stringify(cfg, null, 2));
|
|
1929
1943
|
return path;
|
|
1930
1944
|
}
|
|
1945
|
+
function abortField(abort) {
|
|
1946
|
+
return abort ? { aborted: abort } : {};
|
|
1947
|
+
}
|
|
1931
1948
|
var ISOLATION_FLAGS, MCP_CONFIG_FILE, claudeCodeExecutor;
|
|
1932
1949
|
var init_claudeCode = __esm({
|
|
1933
1950
|
"src/executors/claudeCode.ts"() {
|
|
@@ -1966,7 +1983,10 @@ var init_claudeCode = __esm({
|
|
|
1966
1983
|
durationMs: spend.durationMs,
|
|
1967
1984
|
// TASWM-377: «упёрся в потолок» — отдельный факт об этом же итоге, а не отдельный
|
|
1968
1985
|
// разбор файла: у claude он лежит в тех же полях, что деньги и токены.
|
|
1969
|
-
...budgetExhausted(result) ? { stoppedByBudget: true } : {}
|
|
1986
|
+
...budgetExhausted(result) ? { stoppedByBudget: true } : {},
|
|
1987
|
+
// TASWM-487: и «оборвалось» — третий факт об этом же итоге. Порядок важен: потолок
|
|
1988
|
+
// трат sessionAbort отсекает сам, поэтому оба ключа одновременно не появятся.
|
|
1989
|
+
...abortField(sessionAbort(result))
|
|
1970
1990
|
};
|
|
1971
1991
|
},
|
|
1972
1992
|
session: {
|
|
@@ -2888,11 +2908,11 @@ var init_heartbeat = __esm({
|
|
|
2888
2908
|
});
|
|
2889
2909
|
|
|
2890
2910
|
// src/agentToken.ts
|
|
2891
|
-
import { readFileSync as
|
|
2892
|
-
import { join as
|
|
2911
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, unlinkSync as unlinkSync3 } from "fs";
|
|
2912
|
+
import { join as join8 } from "path";
|
|
2893
2913
|
function loadAgentToken() {
|
|
2894
2914
|
try {
|
|
2895
|
-
const s = JSON.parse(
|
|
2915
|
+
const s = JSON.parse(readFileSync6(STORE_PATH, "utf8"));
|
|
2896
2916
|
return s.token ? s : null;
|
|
2897
2917
|
} catch {
|
|
2898
2918
|
return null;
|
|
@@ -2945,7 +2965,7 @@ var init_agentToken = __esm({
|
|
|
2945
2965
|
"src/agentToken.ts"() {
|
|
2946
2966
|
"use strict";
|
|
2947
2967
|
init_config();
|
|
2948
|
-
STORE_PATH =
|
|
2968
|
+
STORE_PATH = join8(SWARM_HOME, "agent.json");
|
|
2949
2969
|
base = (api) => api.replace(/\/$/, "");
|
|
2950
2970
|
TOKEN_PROBE_TIMEOUT_MS = 8e3;
|
|
2951
2971
|
}
|
|
@@ -3533,7 +3553,7 @@ async function remoteMainRef(repo) {
|
|
|
3533
3553
|
throw new Error(`\u043D\u0435 \u043D\u0430\u0448\u0451\u043B \u0434\u0435\u0444\u043E\u043B\u0442-\u0432\u0435\u0442\u043A\u0443 origin \u0432 ${repo} (\u043D\u0435\u0442 origin/HEAD, origin/main, origin/master)`);
|
|
3534
3554
|
}
|
|
3535
3555
|
async function remoteBranchExists(repo, branch) {
|
|
3536
|
-
const res = await $2({ reject: false })`git -C ${repo} ls-remote --heads origin ${`refs/heads/${branch}`}`;
|
|
3556
|
+
const res = await $2({ reject: false, timeout: 5e3 })`git -C ${repo} ls-remote --heads origin ${`refs/heads/${branch}`}`;
|
|
3537
3557
|
if (res.exitCode !== 0) return null;
|
|
3538
3558
|
return res.stdout.trim().length > 0;
|
|
3539
3559
|
}
|
|
@@ -4527,6 +4547,78 @@ var init_oauth = __esm({
|
|
|
4527
4547
|
}
|
|
4528
4548
|
});
|
|
4529
4549
|
|
|
4550
|
+
// src/toolFailure.ts
|
|
4551
|
+
function trimDetail(s) {
|
|
4552
|
+
const t = s.trim();
|
|
4553
|
+
return t.length <= FAILURE_DETAIL_MAX ? t : `${t.slice(0, FAILURE_DETAIL_MAX)}\u2026`;
|
|
4554
|
+
}
|
|
4555
|
+
function classifyToolFailure(raw) {
|
|
4556
|
+
if (MISMATCH_RE.test(raw)) return TOOL_FAILURE.WORKSPACE_MISMATCH;
|
|
4557
|
+
if (FORBIDDEN_RE.test(raw)) return TOOL_FAILURE.FORBIDDEN;
|
|
4558
|
+
if (NOT_FOUND_RE.test(raw)) return TOOL_FAILURE.NOT_FOUND;
|
|
4559
|
+
return TOOL_FAILURE.TOOL_ERROR;
|
|
4560
|
+
}
|
|
4561
|
+
function classifyThrown(e) {
|
|
4562
|
+
const { kind, detail } = explainLinkFailure(e);
|
|
4563
|
+
const code = kind === "offline" ? TOOL_FAILURE.UNREACHABLE : kind === "unauthorized" ? TOOL_FAILURE.UNAUTHORIZED : (
|
|
4564
|
+
// «ручки нет» и «сервер отказал по существу» разбираем доменно: там может быть
|
|
4565
|
+
// и task_not_found, и отказ по правам — то есть наши коды, а не транспортные.
|
|
4566
|
+
classifyToolFailure(detail)
|
|
4567
|
+
);
|
|
4568
|
+
return { code, detail };
|
|
4569
|
+
}
|
|
4570
|
+
function describeToolFailure(f) {
|
|
4571
|
+
const text = TOOL_FAILURE_TEXT[f.code];
|
|
4572
|
+
const detail = trimDetail(f.detail);
|
|
4573
|
+
return detail ? `${text} (${detail})` : text;
|
|
4574
|
+
}
|
|
4575
|
+
var TOOL_FAILURE, TOOL_FAILURE_TEXT, TOOL_FAILURE_RETRIABLE, FAILURE_DETAIL_MAX, NOT_FOUND_RE, FORBIDDEN_RE, MISMATCH_RE;
|
|
4576
|
+
var init_toolFailure = __esm({
|
|
4577
|
+
"src/toolFailure.ts"() {
|
|
4578
|
+
"use strict";
|
|
4579
|
+
init_linkFailure();
|
|
4580
|
+
TOOL_FAILURE = {
|
|
4581
|
+
/** Тул отработал, но искомого в ответе нет: задачи с таким слагом в воркспейсе не существует. */
|
|
4582
|
+
NOT_FOUND: "not_found",
|
|
4583
|
+
/** Объект есть, но токену роя не виден (приватный проект, спейс, чужой воркспейс). */
|
|
4584
|
+
FORBIDDEN: "forbidden",
|
|
4585
|
+
/** Токен роя не состоит в воркспейсе, которым искали. */
|
|
4586
|
+
WORKSPACE_MISMATCH: "workspace_mismatch",
|
|
4587
|
+
/** До прода не достучались: сеть, DNS, таймаут, оборванный сокет. */
|
|
4588
|
+
UNREACHABLE: "unreachable",
|
|
4589
|
+
/** Прод ответил, но токен роя отвергнут (отозван, протух). Сетью это не лечится. */
|
|
4590
|
+
UNAUTHORIZED: "unauthorized",
|
|
4591
|
+
/** Тул ответил ошибкой, которую мы не узнали, — текст кладём в detail как есть. */
|
|
4592
|
+
TOOL_ERROR: "tool_error",
|
|
4593
|
+
/** Успех, но полезной нагрузки в ответе нет (форма ответа разошлась с ожиданием). */
|
|
4594
|
+
EMPTY: "empty"
|
|
4595
|
+
};
|
|
4596
|
+
TOOL_FAILURE_TEXT = {
|
|
4597
|
+
[TOOL_FAILURE.NOT_FOUND]: "\u0432 \u044D\u0442\u043E\u043C \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0435 \u0442\u0430\u043A\u043E\u0433\u043E \u043D\u0435\u0442 \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044C \u0441\u043B\u0430\u0433 \u0438 \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441 \u043F\u0440\u043E\u0433\u043E\u043D\u0430",
|
|
4598
|
+
[TOOL_FAILURE.FORBIDDEN]: "\u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043D\u0435\u0442: \u043E\u0431\u044A\u0435\u043A\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442, \u043D\u043E \u0442\u043E\u043A\u0435\u043D\u0443 \u0440\u043E\u044F \u043D\u0435 \u0432\u0438\u0434\u0435\u043D \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
4599
|
+
[TOOL_FAILURE.WORKSPACE_MISMATCH]: "\u0442\u043E\u043A\u0435\u043D \u0440\u043E\u044F \u043D\u0435 \u0441\u043E\u0441\u0442\u043E\u0438\u0442 \u0432 \u044D\u0442\u043E\u043C \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0435",
|
|
4600
|
+
[TOOL_FAILURE.UNREACHABLE]: "Taskless \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u2014 \u0441\u0435\u0442\u044C, DNS \u0438\u043B\u0438 \u0442\u0430\u0439\u043C\u0430\u0443\u0442",
|
|
4601
|
+
[TOOL_FAILURE.UNAUTHORIZED]: "\u0442\u043E\u043A\u0435\u043D \u0440\u043E\u044F \u043E\u0442\u0432\u0435\u0440\u0433\u043D\u0443\u0442 \u043F\u0440\u043E\u0434\u043E\u043C \u2014 \u043D\u0443\u0436\u0435\u043D swarm login \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435",
|
|
4602
|
+
[TOOL_FAILURE.TOOL_ERROR]: "\u0442\u0443\u043B \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043E\u0448\u0438\u0431\u043A\u043E\u0439",
|
|
4603
|
+
[TOOL_FAILURE.EMPTY]: "\u0442\u0443\u043B \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0443\u0441\u043F\u0435\u0445\u043E\u043C, \u043D\u043E \u0434\u0430\u043D\u043D\u044B\u0445 \u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u043D\u0435\u0442"
|
|
4604
|
+
};
|
|
4605
|
+
TOOL_FAILURE_RETRIABLE = {
|
|
4606
|
+
[TOOL_FAILURE.NOT_FOUND]: false,
|
|
4607
|
+
[TOOL_FAILURE.FORBIDDEN]: false,
|
|
4608
|
+
[TOOL_FAILURE.WORKSPACE_MISMATCH]: false,
|
|
4609
|
+
[TOOL_FAILURE.UNREACHABLE]: true,
|
|
4610
|
+
// Отозванный токен повтором не чинится — его меняет человек (`swarm login`).
|
|
4611
|
+
[TOOL_FAILURE.UNAUTHORIZED]: false,
|
|
4612
|
+
[TOOL_FAILURE.TOOL_ERROR]: false,
|
|
4613
|
+
[TOOL_FAILURE.EMPTY]: false
|
|
4614
|
+
};
|
|
4615
|
+
FAILURE_DETAIL_MAX = 300;
|
|
4616
|
+
NOT_FOUND_RE = /task_not_found|not_found|не найдена|не найден/i;
|
|
4617
|
+
FORBIDDEN_RE = /forbidden|not visible|no access|нет доступа|(?:http|status|code)[ =:]*403\b|\(403\)|403 forbidden/i;
|
|
4618
|
+
MISMATCH_RE = /workspace_mismatch|not a member of that workspace/i;
|
|
4619
|
+
}
|
|
4620
|
+
});
|
|
4621
|
+
|
|
4530
4622
|
// src/authState.ts
|
|
4531
4623
|
function noteTokenRejected(nowMs = Date.now()) {
|
|
4532
4624
|
if (!denial) {
|
|
@@ -4623,25 +4715,44 @@ function isClosingTaskCategory(category) {
|
|
|
4623
4715
|
function taskAuthor(u) {
|
|
4624
4716
|
return u?.id ? { id: u.id, name: u.displayName || "\u0430\u0432\u0442\u043E\u0440 \u0437\u0430\u0434\u0430\u0447\u0438" } : null;
|
|
4625
4717
|
}
|
|
4718
|
+
function toolErrorText(r) {
|
|
4719
|
+
const fromContent = (r.content ?? []).map((c) => c.text ?? "").filter(Boolean).join(" ").trim();
|
|
4720
|
+
return fromContent || JSON.stringify(r);
|
|
4721
|
+
}
|
|
4626
4722
|
function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl = fetch) {
|
|
4627
4723
|
const ws = workspaceId ? { workspaceId } : {};
|
|
4628
4724
|
const idCache = /* @__PURE__ */ new Map();
|
|
4629
4725
|
const byWorkspace = /* @__PURE__ */ new Map();
|
|
4630
|
-
async function
|
|
4726
|
+
async function callTool(name, args) {
|
|
4631
4727
|
try {
|
|
4632
4728
|
const r = await call(name, WS_FREE_TOOLS.has(name) ? args : { ...args, ...ws });
|
|
4633
4729
|
if (r?.isError) {
|
|
4634
|
-
|
|
4635
|
-
|
|
4730
|
+
const raw = toolErrorText(r);
|
|
4731
|
+
const detail = trimDetail(raw);
|
|
4732
|
+
console.error(`[taskless] \u0442\u0443\u043B ${name} \u0432\u0435\u0440\u043D\u0443\u043B \u043E\u0448\u0438\u0431\u043A\u0443:`, detail);
|
|
4733
|
+
return { ok: false, failure: { code: classifyToolFailure(raw), detail } };
|
|
4636
4734
|
}
|
|
4637
|
-
return r;
|
|
4735
|
+
return { ok: true, res: r };
|
|
4638
4736
|
} catch (e) {
|
|
4639
|
-
|
|
4640
|
-
|
|
4737
|
+
const failure = classifyThrown(e);
|
|
4738
|
+
console.error(`[taskless] \u0442\u0443\u043B ${name} \u0443\u043F\u0430\u043B:`, failure.detail);
|
|
4739
|
+
return { ok: false, failure };
|
|
4641
4740
|
}
|
|
4642
4741
|
}
|
|
4643
|
-
async function
|
|
4644
|
-
const
|
|
4742
|
+
async function safeCall(name, args) {
|
|
4743
|
+
const out = await callTool(name, args);
|
|
4744
|
+
return out.ok ? out.res : null;
|
|
4745
|
+
}
|
|
4746
|
+
async function getTaskDetailed(idOrSlug) {
|
|
4747
|
+
const out = await callTool("get_task", { id: idOrSlug });
|
|
4748
|
+
if (!out.ok) return { ok: false, failure: out.failure };
|
|
4749
|
+
const body = taskBodyFrom(out.res, idOrSlug);
|
|
4750
|
+
if (!body) {
|
|
4751
|
+
return { ok: false, failure: { code: TOOL_FAILURE.EMPTY, detail: "get_task: task \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0432 \u043E\u0442\u0432\u0435\u0442\u0435" } };
|
|
4752
|
+
}
|
|
4753
|
+
return { ok: true, task: body };
|
|
4754
|
+
}
|
|
4755
|
+
function taskBodyFrom(res, idOrSlug) {
|
|
4645
4756
|
const task = res?.structuredContent?.task ?? null;
|
|
4646
4757
|
if (!task?.id) return null;
|
|
4647
4758
|
const slug = task.prefix && task.number != null ? `${task.prefix}-${task.number}` : idOrSlug;
|
|
@@ -4671,6 +4782,9 @@ function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl
|
|
|
4671
4782
|
idCache.set(body.id, body.id);
|
|
4672
4783
|
return body;
|
|
4673
4784
|
}
|
|
4785
|
+
async function getTask(idOrSlug) {
|
|
4786
|
+
return taskBodyFrom(await safeCall("get_task", { id: idOrSlug }), idOrSlug);
|
|
4787
|
+
}
|
|
4674
4788
|
async function getDocument(id) {
|
|
4675
4789
|
const res = await safeCall("get_document", { id });
|
|
4676
4790
|
const doc = res?.structuredContent?.document ?? null;
|
|
@@ -4762,6 +4876,7 @@ function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl
|
|
|
4762
4876
|
}
|
|
4763
4877
|
const self = {
|
|
4764
4878
|
getTask,
|
|
4879
|
+
getTaskDetailed,
|
|
4765
4880
|
...fetchFile ? { fetchFile } : {},
|
|
4766
4881
|
startTask: (idOrSlug) => transition("start_task", idOrSlug),
|
|
4767
4882
|
// TASWM-294: intent — «человек, посмотри», а не «Готово» (см. докблок в TasklessClient).
|
|
@@ -4857,6 +4972,10 @@ function parseWorkspaces(res) {
|
|
|
4857
4972
|
return arr.filter((w) => !!w && typeof w.id === "string" && typeof w.slug === "string").map((w) => ({ id: w.id, slug: w.slug, name: typeof w.name === "string" ? w.name : w.slug, role: w.role }));
|
|
4858
4973
|
}
|
|
4859
4974
|
function requireTaskBody(task, slug, workspaceId) {
|
|
4975
|
+
if (task && "ok" in task) {
|
|
4976
|
+
if (task.ok) return task.task;
|
|
4977
|
+
throw new TaskBodyUnavailableError(slug, workspaceId, task.failure);
|
|
4978
|
+
}
|
|
4860
4979
|
if (!task) throw new TaskBodyUnavailableError(slug, workspaceId);
|
|
4861
4980
|
return task;
|
|
4862
4981
|
}
|
|
@@ -5088,16 +5207,25 @@ var init_taskless = __esm({
|
|
|
5088
5207
|
init_oauth();
|
|
5089
5208
|
init_agentToken();
|
|
5090
5209
|
init_linkFailure();
|
|
5210
|
+
init_toolFailure();
|
|
5091
5211
|
init_authState();
|
|
5092
5212
|
init_workspace();
|
|
5093
5213
|
SLUG_RE = /^[A-Za-z][A-Za-z0-9]*-\d+$/;
|
|
5094
5214
|
WS_FREE_TOOLS = /* @__PURE__ */ new Set(["list_workspaces", "who_am_i", "finalize_upload", "report_acceptance_check"]);
|
|
5095
5215
|
TaskBodyUnavailableError = class extends Error {
|
|
5096
|
-
|
|
5216
|
+
/** Код причины — по нему решают, повторять ли (TOOL_FAILURE_RETRIABLE), а не по тексту. */
|
|
5217
|
+
code;
|
|
5218
|
+
// TASWM-484: причина называется, а не перечисляется. Прежний текст («задачи там нет
|
|
5219
|
+
// ЛИБО токену её не видно») заставлял человека проверять обе версии руками — 21.08 на
|
|
5220
|
+
// этом ушло полдня, и обе оказались не теми. Теперь код приходит от классификатора
|
|
5221
|
+
// ответа прода, а человеку достаётся объяснение и то, что прод сказал дословно.
|
|
5222
|
+
constructor(slug, workspaceId, failure) {
|
|
5223
|
+
const f = failure ?? { code: TOOL_FAILURE.EMPTY, detail: "" };
|
|
5097
5224
|
super(
|
|
5098
|
-
`\u0437\u0430\u0434\u0430\u0447\u0430 \u043D\u0435 \u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F: ${slug} \u0432 \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0435 ${workspaceId || "(\u043D\u0435 \u0437\u0430\u0434\u0430\u043D \u2014 \u0433\u043B\u043E\u0431\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0438\u043D \u0434\u0435\u043C\u043E\u043D\u0430)"} \u2014
|
|
5225
|
+
`\u0437\u0430\u0434\u0430\u0447\u0430 \u043D\u0435 \u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F: ${slug} \u0432 \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0435 ${workspaceId || "(\u043D\u0435 \u0437\u0430\u0434\u0430\u043D \u2014 \u0433\u043B\u043E\u0431\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0438\u043D \u0434\u0435\u043C\u043E\u043D\u0430)"} \u2014 ${describeToolFailure(f)}. \u0414\u0440\u043E\u043D \u041D\u0415 \u043F\u043E\u0434\u043D\u044F\u0442: \u0440\u0430\u0431\u043E\u0442\u0430 \u043F\u043E \u043E\u0434\u043D\u043E\u043C\u0443 \u0441\u043B\u0430\u0433\u0443, \u0431\u0435\u0437 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u044F \u0438 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u0432, \u0434\u043E\u0440\u043E\u0436\u0435 \u0438 \u0432\u0440\u0435\u0434\u043D\u0435\u0435, \u0447\u0435\u043C \u043D\u0435 \u043D\u0430\u0447\u0438\u043D\u0430\u0442\u044C (TASWM-288).`
|
|
5099
5226
|
);
|
|
5100
5227
|
this.name = "TaskBodyUnavailableError";
|
|
5228
|
+
this.code = f.code;
|
|
5101
5229
|
}
|
|
5102
5230
|
};
|
|
5103
5231
|
TASK_CONTEXT_MAX_DOCS = 5;
|
|
@@ -8773,6 +8901,80 @@ function rotateDaemonLog(path, opts, io = realIo) {
|
|
|
8773
8901
|
return "rotated";
|
|
8774
8902
|
}
|
|
8775
8903
|
|
|
8904
|
+
// src/logStamp.ts
|
|
8905
|
+
import { format } from "util";
|
|
8906
|
+
function logStamp(at) {
|
|
8907
|
+
return at.toISOString();
|
|
8908
|
+
}
|
|
8909
|
+
function stampMessage(text, at) {
|
|
8910
|
+
const ts = logStamp(at);
|
|
8911
|
+
return text.split("\n").map((line) => line.length ? `${ts} ${line}` : ts).join("\n");
|
|
8912
|
+
}
|
|
8913
|
+
function utcOffsetText(at) {
|
|
8914
|
+
const minutes = -at.getTimezoneOffset();
|
|
8915
|
+
const sign2 = minutes < 0 ? "-" : "+";
|
|
8916
|
+
const abs = Math.abs(minutes);
|
|
8917
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
8918
|
+
return `UTC${sign2}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`;
|
|
8919
|
+
}
|
|
8920
|
+
function localZoneLine(at, zone) {
|
|
8921
|
+
const tz = zone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
8922
|
+
return `[overmind] \u0432\u0440\u0435\u043C\u044F \u0432 \u043B\u043E\u0433\u0435 \u2014 UTC, ISO-8601 \u0441 \u043C\u0438\u043B\u043B\u0438\u0441\u0435\u043A\u0443\u043D\u0434\u0430\u043C\u0438 (${logStamp(at)}); \u043C\u0435\u0441\u0442\u043D\u043E\u0435 \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 ${utcOffsetText(at)}${tz ? ` (${tz})` : ""}`;
|
|
8923
|
+
}
|
|
8924
|
+
var LEVELS = ["log", "info", "warn", "error"];
|
|
8925
|
+
var installed = false;
|
|
8926
|
+
function installLogStamps(now = () => /* @__PURE__ */ new Date()) {
|
|
8927
|
+
if (installed) return () => {
|
|
8928
|
+
};
|
|
8929
|
+
installed = true;
|
|
8930
|
+
const original = LEVELS.map((level) => [level, console[level]]);
|
|
8931
|
+
for (const [level, orig] of original) {
|
|
8932
|
+
const write = orig.bind(console);
|
|
8933
|
+
console[level] = (...args) => {
|
|
8934
|
+
write(stampMessage(format(...args), now()));
|
|
8935
|
+
};
|
|
8936
|
+
}
|
|
8937
|
+
return () => {
|
|
8938
|
+
for (const [level, orig] of original) console[level] = orig;
|
|
8939
|
+
installed = false;
|
|
8940
|
+
};
|
|
8941
|
+
}
|
|
8942
|
+
|
|
8943
|
+
// src/logNoise.ts
|
|
8944
|
+
var DEFAULT_REMIND_MS = 10 * 6e4;
|
|
8945
|
+
function timesWord(n) {
|
|
8946
|
+
const tens = n % 100;
|
|
8947
|
+
if (tens >= 11 && tens <= 14) return "\u0440\u0430\u0437";
|
|
8948
|
+
return n % 10 >= 2 && n % 10 <= 4 ? "\u0440\u0430\u0437\u0430" : "\u0440\u0430\u0437";
|
|
8949
|
+
}
|
|
8950
|
+
function makeRepeatFilter(opts = {}) {
|
|
8951
|
+
const remindAfterMs = opts.remindAfterMs ?? DEFAULT_REMIND_MS;
|
|
8952
|
+
const streaks = /* @__PURE__ */ new Map();
|
|
8953
|
+
return {
|
|
8954
|
+
say(key, message, now) {
|
|
8955
|
+
const prev = streaks.get(key);
|
|
8956
|
+
if (!prev || prev.message !== message) {
|
|
8957
|
+
const tail2 = prev && prev.hidden > 0 ? ` (\u0434\u043E \u044D\u0442\u043E\u0433\u043E \u0442\u043E \u0436\u0435 \u0441\u0430\u043C\u043E\u0435 \u043C\u043E\u043B\u0447\u0430 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u043B\u043E\u0441\u044C \u0435\u0449\u0451 ${prev.hidden} ${timesWord(prev.hidden)})` : "";
|
|
8958
|
+
streaks.set(key, { message, since: now, saidAt: now, hidden: 0 });
|
|
8959
|
+
return `${message}${tail2}`;
|
|
8960
|
+
}
|
|
8961
|
+
prev.hidden += 1;
|
|
8962
|
+
if (now - prev.saidAt < remindAfterMs) return null;
|
|
8963
|
+
const hidden = prev.hidden;
|
|
8964
|
+
const minutes = Math.max(1, Math.round((now - prev.since) / 6e4));
|
|
8965
|
+
prev.saidAt = now;
|
|
8966
|
+
prev.hidden = 0;
|
|
8967
|
+
return `${message} \u2014 \u0442\u043E \u0436\u0435 \u0441\u0430\u043C\u043E\u0435 ${hidden} ${timesWord(hidden)} \u043F\u043E\u0434\u0440\u044F\u0434, \u0442\u044F\u043D\u0435\u0442\u0441\u044F ${minutes} \u043C\u0438\u043D`;
|
|
8968
|
+
},
|
|
8969
|
+
forget(key) {
|
|
8970
|
+
streaks.delete(key);
|
|
8971
|
+
}
|
|
8972
|
+
};
|
|
8973
|
+
}
|
|
8974
|
+
|
|
8975
|
+
// src/overmind.ts
|
|
8976
|
+
init_linkFailure();
|
|
8977
|
+
|
|
8776
8978
|
// src/repoRegistry.ts
|
|
8777
8979
|
init_config();
|
|
8778
8980
|
import { $ } from "execa";
|
|
@@ -8811,13 +9013,13 @@ async function buildRepoRegistry(paths) {
|
|
|
8811
9013
|
}
|
|
8812
9014
|
|
|
8813
9015
|
// src/repoMemory.ts
|
|
8814
|
-
import { readFileSync as
|
|
8815
|
-
import { join as
|
|
9016
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
9017
|
+
import { join as join9 } from "path";
|
|
8816
9018
|
var FILE = "repos.json";
|
|
8817
9019
|
function loadRememberedRepos(home) {
|
|
8818
9020
|
let raw;
|
|
8819
9021
|
try {
|
|
8820
|
-
raw =
|
|
9022
|
+
raw = readFileSync7(join9(home, FILE), "utf8");
|
|
8821
9023
|
} catch {
|
|
8822
9024
|
return [];
|
|
8823
9025
|
}
|
|
@@ -8832,7 +9034,7 @@ function loadRememberedRepos(home) {
|
|
|
8832
9034
|
function rememberRepos(home, paths, now = () => /* @__PURE__ */ new Date()) {
|
|
8833
9035
|
const clean = paths.filter((p) => p.trim().length > 0);
|
|
8834
9036
|
if (clean.length === 0) return;
|
|
8835
|
-
|
|
9037
|
+
writeFileSync7(join9(home, FILE), `${JSON.stringify({ paths: clean, at: now().toISOString() }, null, 2)}
|
|
8836
9038
|
`);
|
|
8837
9039
|
}
|
|
8838
9040
|
function pickRepoPaths(args) {
|
|
@@ -9077,9 +9279,13 @@ async function refreshRepoMap(opts) {
|
|
|
9077
9279
|
added++;
|
|
9078
9280
|
}
|
|
9079
9281
|
scannedAt = stamp();
|
|
9080
|
-
|
|
9081
|
-
|
|
9082
|
-
|
|
9282
|
+
const outcome = `[overmind] \u043F\u0440\u043E\u0448\u0451\u043B \u043F\u043E \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430\u043C (${SCAN_REASON_TEXT[decision.reason]}): ${added > 0 ? `\u043D\u0430\u0448\u0451\u043B \u0435\u0449\u0451 ${added}` : "\u043D\u043E\u0432\u043E\u0433\u043E \u043D\u0435 \u043D\u0430\u0448\u0451\u043B"}, \u0432\u0441\u0435\u0433\u043E \u043A\u043B\u043E\u043D\u043E\u0432 ${clones.size}`;
|
|
9283
|
+
if (added > 0 || opts.state.announcedScan !== outcome) {
|
|
9284
|
+
opts.state.announcedScan = outcome;
|
|
9285
|
+
log(
|
|
9286
|
+
`${outcome}, \u0437\u0430 ${Date.now() - startedAt}\u043C\u0441. \u0418\u0441\u043A\u0430\u043B \u0432: ${opts.roots.length ? opts.roots.slice(0, 6).join(", ") : "\u043D\u0438\u0433\u0434\u0435 \u2014 \u043F\u043E\u0438\u0441\u043A \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D (SWARM_SCAN_ROOTS)"}`
|
|
9287
|
+
);
|
|
9288
|
+
}
|
|
9083
9289
|
}
|
|
9084
9290
|
const registry = [...clones.values()].map((c) => ({
|
|
9085
9291
|
repoFullName: c.repoFullName,
|
|
@@ -9091,8 +9297,10 @@ async function refreshRepoMap(opts) {
|
|
|
9091
9297
|
scannedAt
|
|
9092
9298
|
);
|
|
9093
9299
|
if (!answer.ok) {
|
|
9094
|
-
|
|
9095
|
-
|
|
9300
|
+
announceMap(opts.state, log, [
|
|
9301
|
+
describeReposFailure(answer.kind, answer.detail),
|
|
9302
|
+
`[overmind] \u0438\u0442\u043E\u0433: \u043A\u043B\u043E\u043D\u043E\u0432 \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E ${clones.size}, \u0441\u043F\u0438\u0441\u043E\u043A \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0435\u0432 \u0440\u043E\u044F \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043D (\u0441\u043C. \u0441\u0442\u0440\u043E\u043A\u0443 \u0432\u044B\u0448\u0435)`
|
|
9303
|
+
]);
|
|
9096
9304
|
return registry;
|
|
9097
9305
|
}
|
|
9098
9306
|
opts.state.unknown = false;
|
|
@@ -9101,18 +9309,39 @@ async function refreshRepoMap(opts) {
|
|
|
9101
9309
|
opts.state.roots = [...opts.roots];
|
|
9102
9310
|
opts.state.at = stamp();
|
|
9103
9311
|
if (wantedNames.length === 0) {
|
|
9104
|
-
log
|
|
9312
|
+
announceMap(opts.state, log, ["[overmind] \u0443 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430 \u043D\u0435\u0442 \u043F\u0440\u043E\u0435\u043A\u0442\u043E\u0432 \u0441 \u0432\u043A\u043B\u044E\u0447\u0451\u043D\u043D\u044B\u043C \u0440\u043E\u0435\u043C \u2014 \u0440\u0430\u0431\u043E\u0442\u0430\u044E \u043F\u043E \u043D\u0430\u0439\u0434\u0435\u043D\u043D\u043E\u043C\u0443 \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435"]);
|
|
9105
9313
|
return registry;
|
|
9106
9314
|
}
|
|
9107
|
-
log(`[overmind] ${describeMapSummary(opts.state, opts.roots)}`);
|
|
9108
|
-
for (const c of opts.state.clones) log(`[overmind] ${describeClone(c)}`);
|
|
9109
9315
|
const prefixOf = new Map(answer.repos.map((r) => [r.repoFullName, r.taskPrefix]));
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
log
|
|
9113
|
-
|
|
9316
|
+
announceMap(
|
|
9317
|
+
opts.state,
|
|
9318
|
+
log,
|
|
9319
|
+
[
|
|
9320
|
+
`[overmind] ${describeMapSummary(opts.state, opts.roots)}`,
|
|
9321
|
+
...opts.state.clones.map((c) => `[overmind] ${describeClone(c)}`),
|
|
9322
|
+
...opts.state.missing.map((name) => {
|
|
9323
|
+
const prefix = prefixOf.get(name);
|
|
9324
|
+
return `[overmind] ${describeMissing(prefix ? `${name} (\u043F\u0440\u043E\u0435\u043A\u0442 ${prefix})` : name, opts.roots)}`;
|
|
9325
|
+
})
|
|
9326
|
+
],
|
|
9327
|
+
mapSignature(opts.state, opts.roots)
|
|
9328
|
+
);
|
|
9114
9329
|
return registry;
|
|
9115
9330
|
}
|
|
9331
|
+
function announceMap(state, log, lines, signature = lines.join("\n")) {
|
|
9332
|
+
if (state.announced === signature) return;
|
|
9333
|
+
const first = state.announced === void 0;
|
|
9334
|
+
state.announced = signature;
|
|
9335
|
+
if (!first) log("[overmind] \u043A\u0430\u0440\u0442\u0430 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0435\u0432 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0430\u0441\u044C:");
|
|
9336
|
+
for (const line of lines) log(line);
|
|
9337
|
+
}
|
|
9338
|
+
function mapSignature(state, roots) {
|
|
9339
|
+
return [
|
|
9340
|
+
...state.clones.map((c) => `${c.repoFullName}=${c.path}${c.alternatives.length ? `+${c.alternatives.join("+")}` : ""}`),
|
|
9341
|
+
`missing:${state.missing.join(",")}`,
|
|
9342
|
+
`roots:${roots.join(",")}`
|
|
9343
|
+
].join("\n");
|
|
9344
|
+
}
|
|
9116
9345
|
function describeMapSummary(state, roots) {
|
|
9117
9346
|
const found = state.clones.length;
|
|
9118
9347
|
const where = roots.length ? roots.slice(0, 6).join(", ") : "\u043F\u043E\u0438\u0441\u043A \u043F\u043E \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430\u043C \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D (SWARM_SCAN_ROOTS)";
|
|
@@ -9809,17 +10038,20 @@ import { readdirSync as readdirSync10, readFileSync as readFileSync12 } from "fs
|
|
|
9809
10038
|
import { join as join16 } from "path";
|
|
9810
10039
|
init_executors();
|
|
9811
10040
|
function endedItself(outcome) {
|
|
9812
|
-
return outcome === "graceful" || outcome === "budget";
|
|
10041
|
+
return outcome === "graceful" || outcome === "budget" || outcome === "aborted";
|
|
10042
|
+
}
|
|
10043
|
+
function wasAborted(outcome) {
|
|
10044
|
+
return outcome === "aborted";
|
|
9813
10045
|
}
|
|
9814
10046
|
var RESULT_FILE_RE = /^(\d+)\.json(\.done)?$/;
|
|
9815
|
-
function
|
|
10047
|
+
function readLatestResultRaw(runLogsDir, runLogKeys) {
|
|
9816
10048
|
const dir = findRunLogDir(runLogsDir, runLogKeys);
|
|
9817
|
-
if (!dir) return
|
|
10049
|
+
if (!dir) return null;
|
|
9818
10050
|
let files;
|
|
9819
10051
|
try {
|
|
9820
10052
|
files = readdirSync10(dir);
|
|
9821
10053
|
} catch {
|
|
9822
|
-
return
|
|
10054
|
+
return null;
|
|
9823
10055
|
}
|
|
9824
10056
|
let latest = null;
|
|
9825
10057
|
for (const f of files) {
|
|
@@ -9828,21 +10060,35 @@ function classifyExit(runLogsDir, runLogKeys, executor = resolveExecutor()) {
|
|
|
9828
10060
|
const idx = Number(m[1]);
|
|
9829
10061
|
if (!latest || idx > latest.idx) latest = { idx, name: f };
|
|
9830
10062
|
}
|
|
9831
|
-
if (!latest) return
|
|
9832
|
-
let raw;
|
|
10063
|
+
if (!latest) return null;
|
|
9833
10064
|
try {
|
|
9834
|
-
|
|
10065
|
+
return readFileSync12(join16(dir, latest.name), "utf8");
|
|
9835
10066
|
} catch {
|
|
9836
|
-
return
|
|
10067
|
+
return null;
|
|
9837
10068
|
}
|
|
10069
|
+
}
|
|
10070
|
+
function classifyExit(runLogsDir, runLogKeys, executor = resolveExecutor()) {
|
|
10071
|
+
const raw = readLatestResultRaw(runLogsDir, runLogKeys);
|
|
10072
|
+
if (raw == null) return "unknown";
|
|
9838
10073
|
const result = executor.parseResult(raw, null);
|
|
9839
10074
|
if (!result) return "crash";
|
|
9840
|
-
|
|
10075
|
+
if (result.stoppedByBudget) return "budget";
|
|
10076
|
+
if (result.aborted) return "aborted";
|
|
10077
|
+
return "graceful";
|
|
10078
|
+
}
|
|
10079
|
+
function abortOfExit(runLogsDir, runLogKeys, executor = resolveExecutor()) {
|
|
10080
|
+
const raw = readLatestResultRaw(runLogsDir, runLogKeys);
|
|
10081
|
+
if (raw == null) return null;
|
|
10082
|
+
return executor.parseResult(raw, null)?.aborted ?? null;
|
|
9841
10083
|
}
|
|
9842
10084
|
function makeExitClassifier(runLogsDir, runLogKeys, executor) {
|
|
9843
10085
|
const exec = executor ?? resolveExecutor();
|
|
9844
10086
|
return (run) => classifyExit(runLogsDir, runLogKeys(run.id), exec);
|
|
9845
10087
|
}
|
|
10088
|
+
function makeAbortReader(runLogsDir, runLogKeys, executor) {
|
|
10089
|
+
const exec = executor ?? resolveExecutor();
|
|
10090
|
+
return (run) => abortOfExit(runLogsDir, runLogKeys(run.id), exec);
|
|
10091
|
+
}
|
|
9846
10092
|
|
|
9847
10093
|
// src/orchestrator.ts
|
|
9848
10094
|
init_src();
|
|
@@ -10768,10 +11014,24 @@ function fullCommitHeadline(headline, body) {
|
|
|
10768
11014
|
return head.slice(0, -1) + tail2;
|
|
10769
11015
|
}
|
|
10770
11016
|
var NO_CHECKS_STDERR = /no checks reported/i;
|
|
11017
|
+
var CiAccessDeniedError = class extends Error {
|
|
11018
|
+
detail;
|
|
11019
|
+
constructor(detail) {
|
|
11020
|
+
super(`\u043D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043A \u0447\u0435\u043A\u0430\u043C: ${detail}`);
|
|
11021
|
+
this.name = "CiAccessDeniedError";
|
|
11022
|
+
this.detail = detail;
|
|
11023
|
+
}
|
|
11024
|
+
};
|
|
11025
|
+
var DENIED_STDERR = /(?:^|\D)403(?:\D|$)|not accessible by integration|must have admin rights|resource not accessible/i;
|
|
11026
|
+
function firstLine(s) {
|
|
11027
|
+
return s.split("\n").map((l) => l.trim()).find((l) => l.length > 0) ?? "gh \u0432\u0435\u0440\u043D\u0443\u043B 403";
|
|
11028
|
+
}
|
|
10771
11029
|
var realChecksRunner = async (pr) => {
|
|
10772
11030
|
const r = await $3({ reject: false, timeout: 15e3 })`gh pr checks ${pr} --json state,name,bucket,link`;
|
|
10773
11031
|
const stdout = r.stdout ?? "";
|
|
10774
|
-
|
|
11032
|
+
const stderr = r.stderr ?? "";
|
|
11033
|
+
if (!stdout.trim() && NO_CHECKS_STDERR.test(stderr)) return "[]";
|
|
11034
|
+
if (!stdout.trim() && DENIED_STDERR.test(stderr)) throw new CiAccessDeniedError(firstLine(stderr));
|
|
10775
11035
|
return stdout;
|
|
10776
11036
|
};
|
|
10777
11037
|
async function prCheckRows(pr, run) {
|
|
@@ -10780,13 +11040,17 @@ async function prCheckRows(pr, run) {
|
|
|
10780
11040
|
const rows = JSON.parse(out);
|
|
10781
11041
|
if (!Array.isArray(rows)) return null;
|
|
10782
11042
|
return rows.map((r) => ({ name: r.name ?? "?", bucket: (r.bucket ?? "").toLowerCase(), link: r.link ?? "" }));
|
|
10783
|
-
} catch {
|
|
11043
|
+
} catch (e) {
|
|
11044
|
+
if (e instanceof CiAccessDeniedError) return { denied: e.detail };
|
|
10784
11045
|
return null;
|
|
10785
11046
|
}
|
|
10786
11047
|
}
|
|
11048
|
+
function isDenied(rows) {
|
|
11049
|
+
return rows != null && !Array.isArray(rows);
|
|
11050
|
+
}
|
|
10787
11051
|
async function ghChecks(pr, run = realChecksRunner) {
|
|
10788
11052
|
const rows = await prCheckRows(pr, run);
|
|
10789
|
-
if (!rows || rows.length === 0) return null;
|
|
11053
|
+
if (!rows || isDenied(rows) || rows.length === 0) return null;
|
|
10790
11054
|
let pass = 0, fail = 0, pending2 = 0;
|
|
10791
11055
|
for (const r of rows) {
|
|
10792
11056
|
if (r.bucket === "pass") pass++;
|
|
@@ -10817,6 +11081,7 @@ function runIdFromLink(link) {
|
|
|
10817
11081
|
async function prCiStatus(pr, run = realChecksRunner) {
|
|
10818
11082
|
const rows = await prCheckRows(pr, run);
|
|
10819
11083
|
if (!rows) return null;
|
|
11084
|
+
if (isDenied(rows)) return { bucket: "denied", failing: [], failingRunIds: [], total: 0, pass: 0, pending: 0, detail: rows.denied };
|
|
10820
11085
|
if (rows.length === 0) return { bucket: "none", failing: [], failingRunIds: [], total: 0, pass: 0, pending: 0 };
|
|
10821
11086
|
const total = rows.length;
|
|
10822
11087
|
const pass = rows.filter((r) => r.bucket === "pass").length;
|
|
@@ -13312,7 +13577,7 @@ async function changedPaths(worktree, opts = {}) {
|
|
|
13312
13577
|
// package.json
|
|
13313
13578
|
var package_default = {
|
|
13314
13579
|
name: "@taskless-app/swarm",
|
|
13315
|
-
version: "0.6.
|
|
13580
|
+
version: "0.6.22",
|
|
13316
13581
|
type: "module",
|
|
13317
13582
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
13318
13583
|
keywords: [
|
|
@@ -13362,7 +13627,7 @@ var package_default = {
|
|
|
13362
13627
|
execa: "^10.0.0",
|
|
13363
13628
|
zod: "3.25.76"
|
|
13364
13629
|
},
|
|
13365
|
-
gitHead: "
|
|
13630
|
+
gitHead: "b8eaac4a5562ffcf45a02af338267620e0a7843f"
|
|
13366
13631
|
};
|
|
13367
13632
|
|
|
13368
13633
|
// src/version.ts
|
|
@@ -15218,9 +15483,9 @@ async function runSelfUpdate(io) {
|
|
|
15218
15483
|
if (!exists(join28(repo, "pnpm-lock.yaml"))) {
|
|
15219
15484
|
return { ok: false, step: "install", message: "\u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0435\u0442 pnpm-lock.yaml \u2014 \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u0435\u0433\u043E \u044D\u0442\u0438\u043C \u043F\u0443\u0442\u0451\u043C \u043D\u0435 \u0431\u0435\u0440\u0443\u0441\u044C" };
|
|
15220
15485
|
}
|
|
15221
|
-
const
|
|
15222
|
-
if (
|
|
15223
|
-
return { ok: false, step: "install", message: `pnpm install \u043D\u0435 \u043F\u0440\u043E\u0448\u0451\u043B: ${tail(
|
|
15486
|
+
const installed2 = await run("pnpm", ["install", "--frozen-lockfile"], repo);
|
|
15487
|
+
if (installed2.exitCode !== 0) {
|
|
15488
|
+
return { ok: false, step: "install", message: `pnpm install \u043D\u0435 \u043F\u0440\u043E\u0448\u0451\u043B: ${tail(installed2.stderr || installed2.stdout)}` };
|
|
15224
15489
|
}
|
|
15225
15490
|
await io.onStep("build", "\u0441\u043E\u0431\u0438\u0440\u0430\u044E \u043F\u0430\u043A\u0435\u0442\u044B \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0430");
|
|
15226
15491
|
const built = await run("pnpm", ["--filter", `${DAEMON_PACKAGE}...`, "build"], repo);
|
|
@@ -15843,9 +16108,24 @@ var executorHasSession = (p) => EXECUTOR.session.exists(p);
|
|
|
15843
16108
|
var OVERMIND_ENTRY = THIS_FILE;
|
|
15844
16109
|
var STALL_MS = Number(process.env.SWARM_STALL_MS) || 10 * 60 * 1e3;
|
|
15845
16110
|
var STALL_FLOOR_MS = Number(process.env.SWARM_STALL_FLOOR_MS) || 30 * 60 * 1e3;
|
|
16111
|
+
var DEAD_HOLDER_SILENCE_MS = Number(process.env.SWARM_DEAD_HOLDER_SILENCE_MS) || 3 * 60 * 60 * 1e3;
|
|
15846
16112
|
var RESPAWN_LIMIT = Number(process.env.SWARM_RESPAWN_LIMIT) || 2;
|
|
15847
16113
|
var RESPAWN_YOUNG_MS = Number(process.env.SWARM_RESPAWN_YOUNG_MS) || 60 * 1e3;
|
|
15848
|
-
var EXITED_NO_DONE_REASON = "\u0441\u0435\u0441\u0441\u0438\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0430\u0441\u044C \u0441\u0430\u043C\u0430, done() \u043D\u0435 \u0432\u044B\u0437\u0432\u0430\u043D
|
|
16114
|
+
var EXITED_NO_DONE_REASON = "\u0441\u0435\u0441\u0441\u0438\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0430\u0441\u044C \u0441\u0430\u043C\u0430, done() \u043D\u0435 \u0432\u044B\u0437\u0432\u0430\u043D";
|
|
16115
|
+
var ABORTED_STOP_REASON = "\u0441\u0435\u0441\u0441\u0438\u044F \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C, done() \u043D\u0435 \u0432\u044B\u0437\u0432\u0430\u043D";
|
|
16116
|
+
var EXIT_WORK_PUSHED = "\u0432\u0435\u0442\u043A\u0430 \u0435\u0441\u0442\u044C \u043D\u0430 origin \u2014 \u0435\u0441\u0442\u044C \u0447\u0442\u043E \u0441\u043F\u0430\u0441\u0430\u0442\u044C, PR \u043D\u0435 \u043E\u0442\u043A\u0440\u044B\u0442";
|
|
16117
|
+
var EXIT_WORK_LOST = "\u0432\u0435\u0442\u043A\u0438 \u043D\u0430 origin \u043D\u0435\u0442 \u2014 \u043D\u0435\u0437\u0430\u043F\u0443\u0448\u0435\u043D\u043D\u043E\u0435 \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u043E";
|
|
16118
|
+
var EXIT_WORK_UNKNOWN = "\u0441\u043F\u0440\u043E\u0441\u0438\u0442\u044C origin \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u2014 \u0446\u0435\u043B\u0430 \u043B\u0438 \u0440\u0430\u0431\u043E\u0442\u0430, \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E";
|
|
16119
|
+
var ABORT_TEXT = {
|
|
16120
|
+
tools: "\u0441\u0435\u0441\u0441\u0438\u044F \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C \u043D\u0430 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u0430 \u2014 \u043E\u0431\u044B\u0447\u043D\u043E \u044D\u0442\u043E \u043A\u043E\u043C\u0430\u043D\u0434\u0430 \u0441 \u0433\u0438\u0433\u0430\u043D\u0442\u0441\u043A\u0438\u043C \u0432\u044B\u0432\u043E\u0434\u043E\u043C (\u043F\u043E\u043B\u043D\u044B\u0439 \u0441\u044C\u044E\u0442 \u0442\u0435\u0441\u0442\u043E\u0432, \u043B\u043E\u0433 \u0431\u0435\u0437 \u043B\u0438\u043C\u0438\u0442\u0430): \u043E\u0442\u0432\u0435\u0442 \u0442\u0443\u043B\u0430 \u043D\u0435 \u0432\u043B\u0435\u0437\u0430\u0435\u0442 \u0438 \u043F\u0440\u0438\u0445\u043E\u0434\u0438\u0442 \u043F\u0443\u0441\u0442\u044B\u043C",
|
|
16121
|
+
link: "\u0441\u0435\u0441\u0441\u0438\u044F \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C \u043D\u0430 \u0441\u0432\u044F\u0437\u0438 \u0441 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u043E\u043C (\u0441\u0442\u0440\u0438\u043C/\u043E\u0448\u0438\u0431\u043A\u0430 API) \u2014 \u043B\u0435\u0447\u0438\u0442\u0441\u044F \u043F\u043E\u0432\u0442\u043E\u0440\u043E\u043C",
|
|
16122
|
+
other: "\u0441\u0435\u0441\u0441\u0438\u044F \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F"
|
|
16123
|
+
};
|
|
16124
|
+
function abortReason(abort) {
|
|
16125
|
+
if (!abort) return "\u0441\u0435\u0441\u0441\u0438\u044F \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C";
|
|
16126
|
+
return `${ABORT_TEXT[abort.kind]} (${abort.raw})`;
|
|
16127
|
+
}
|
|
16128
|
+
var ABORTED_CONTINUE_NUDGE = "\u0422\u0432\u043E\u044F \u043F\u0440\u043E\u0448\u043B\u0430\u044F \u0441\u0435\u0441\u0441\u0438\u044F \u041E\u0411\u041E\u0420\u0412\u0410\u041B\u0410\u0421\u042C \u043D\u0430 \u043F\u043E\u043B\u0443\u0441\u043B\u043E\u0432\u0435 (\u043D\u0435 \u0442\u044B \u0435\u0451 \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B). \u0422\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`: \u0447\u0442\u043E \u0443\u0436\u0435 \u0441\u0434\u0435\u043B\u0430\u043D\u043E \u0438 \u0437\u0430\u043F\u0443\u0448\u0435\u043D\u043E \u2014 \u041D\u0415 \u043F\u0435\u0440\u0435\u0434\u0435\u043B\u044B\u0432\u0430\u0439. \u0412\u0410\u0416\u041D\u041E \u043F\u0440\u043E \u043E\u0431\u0440\u044B\u0432\u044B: \u0440\u0432\u0451\u0442\u0441\u044F \u0441\u0435\u0441\u0441\u0438\u044F \u043D\u0430 \u041E\u0422\u0412\u0415\u0422\u0415 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u0430, \u0447\u0435\u0439 \u0432\u044B\u0432\u043E\u0434 \u043E\u0433\u0440\u043E\u043C\u0435\u043D (\u043F\u043E\u043B\u043D\u044B\u0439 \u0441\u044C\u044E\u0442 \u0442\u0435\u0441\u0442\u043E\u0432, `git log` \u0431\u0435\u0437 \u043B\u0438\u043C\u0438\u0442\u0430, \u0434\u0430\u043C\u043F \u0444\u0430\u0439\u043B\u0430 \u0446\u0435\u043B\u0438\u043A\u043E\u043C). \u0413\u043D\u0430\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043D\u0430\u0434\u043E \u043F\u043E-\u043F\u0440\u0435\u0436\u043D\u0435\u043C\u0443 \u043F\u043E\u043B\u043D\u043E\u0441\u0442\u044C\u044E \u2014 \u043D\u043E \u0432\u044B\u0432\u043E\u0434 \u043F\u0438\u0448\u0438 \u0432 \u0441\u0432\u043E\u0439 \u0444\u0430\u0439\u043B (`LOG=$(mktemp -t suite)`) \u0438 \u0431\u0435\u0440\u0438 \u0438\u0437 \u043D\u0435\u0433\u043E \u0445\u0432\u043E\u0441\u0442 \u0438 \u0438\u0442\u043E\u0433\u043E\u0432\u0443\u044E \u0441\u0442\u0440\u043E\u043A\u0443, \u0430 \u043D\u0435 \u043F\u0440\u043E\u0441\u0442\u044B\u043D\u044E. \u0414\u043E\u0432\u0435\u0434\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u0434\u043E \u043A\u043E\u043D\u0446\u0430, \u043E\u0442\u043A\u0440\u043E\u0439 PR \u0438 \u041E\u0411\u042F\u0417\u0410\u0422\u0415\u041B\u042C\u041D\u041E \u0432\u044B\u0437\u043E\u0432\u0438 done(pr_url).";
|
|
15849
16129
|
var EXITED_CONTINUE_NUDGE = "\u0422\u0432\u043E\u044F \u043F\u0440\u043E\u0448\u043B\u0430\u044F \u0441\u0435\u0441\u0441\u0438\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0430\u0441\u044C \u0421\u0410\u041C\u0410, \u043D\u043E \u0442\u044B \u0442\u0430\u043A \u0438 \u043D\u0435 \u0432\u044B\u0437\u0432\u0430\u043B done(pr_url) \u2014 \u0440\u043E\u0439 \u043D\u0435 \u0443\u0432\u0438\u0434\u0435\u043B \u0441\u0434\u0430\u0447\u0438. \u0422\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`: \u0447\u0442\u043E \u0443\u0436\u0435 \u0441\u0434\u0435\u043B\u0430\u043D\u043E \u0438 \u0437\u0430\u043F\u0443\u0448\u0435\u043D\u043E \u2014 \u041D\u0415 \u043F\u0435\u0440\u0435\u0434\u0435\u043B\u044B\u0432\u0430\u0439. \u0414\u043E\u0432\u0435\u0434\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u0434\u043E \u043A\u043E\u043D\u0446\u0430, \u043E\u0442\u043A\u0440\u043E\u0439 PR (\u0435\u0441\u043B\u0438 \u0435\u0449\u0451 \u043D\u0435\u0442) \u0438 \u041E\u0411\u042F\u0417\u0410\u0422\u0415\u041B\u042C\u041D\u041E \u0432\u044B\u0437\u043E\u0432\u0438 done(pr_url) \u2014 \u0431\u0435\u0437 \u044D\u0442\u043E\u0433\u043E \u0440\u0430\u0431\u043E\u0442\u0430 \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F \u0441\u0434\u0430\u043D\u043D\u043E\u0439. \u0423\u0442\u043A\u043D\u0443\u043B\u0441\u044F \u0432 \u043B\u0438\u043C\u0438\u0442/\u043D\u0435 \u043C\u043E\u0436\u0435\u0448\u044C \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u0442\u044C \u2014 \u043D\u0435 \u043C\u043E\u043B\u043E\u0442\u0438 \u0432\u0441\u043B\u0435\u043F\u0443\u044E, \u0437\u0430\u0434\u0430\u0439 \u0432\u043E\u043F\u0440\u043E\u0441 \u0447\u0435\u0440\u0435\u0437 MCP-\u0442\u0443\u043B ask. \u0412\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u0432 stdout \u043D\u0435 \u0437\u0430\u0434\u0430\u0432\u0430\u0439.";
|
|
15850
16130
|
var STORM_DEAD_LIMIT = Number(process.env.SWARM_STORM_DEAD_LIMIT) || 3;
|
|
15851
16131
|
var CONFLICT_RESOLVE_COOLDOWN_MS = Number(process.env.SWARM_CONFLICT_COOLDOWN_MS) || 3 * 60 * 1e3;
|
|
@@ -15866,6 +16146,9 @@ var CI_WAKE_ESCALATION_MARK = "CI-\u0430\u0432\u0442\u043E\u043F\u043E\u0447\u04
|
|
|
15866
16146
|
var CI_NO_CHECKS_MARK = "\u0443 PR \u043D\u0435\u0442 \u0447\u0435\u043A\u043E\u0432 \u0438 \u043D\u0435 \u0431\u0443\u0434\u0435\u0442";
|
|
15867
16147
|
var CI_WAIT_POLL_MARK = "\u0436\u0434\u0443 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A";
|
|
15868
16148
|
var CI_POLL_LIMIT_MARK = "\u043E\u043F\u0440\u043E\u0441 CI \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D \u043F\u043E \u043F\u043E\u0442\u043E\u043B\u043A\u0443";
|
|
16149
|
+
var CI_DENIED_LIMIT = Number(process.env.SWARM_CI_DENIED_LIMIT) || 3;
|
|
16150
|
+
var CI_DENIED_MARK = "\u043D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043A \u0447\u0435\u043A\u0430\u043C (403)";
|
|
16151
|
+
var CI_DENIED_STOP_MARK = "\u043E\u043F\u0440\u043E\u0441 CI \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D: \u043D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 (403)";
|
|
15869
16152
|
var CI_WAIT_LIMIT_MS = Number(process.env.SWARM_CI_WAIT_LIMIT_MS) || 45 * 60 * 1e3;
|
|
15870
16153
|
var CI_POLL_LIMIT = Math.max(
|
|
15871
16154
|
Number(process.env.SWARM_CI_POLL_LIMIT) || 90,
|
|
@@ -15883,7 +16166,7 @@ var CHANGES_WAKE_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430
|
|
|
15883
16166
|
var CHANGES_WAKE_ESCALATION_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0430\u0441\u044C";
|
|
15884
16167
|
var CHANGES_WAKE_FAILED_MARK = "\u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C";
|
|
15885
16168
|
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";
|
|
15886
|
-
var NO_AUTO_WAKE_REASONS = [EXITED_NO_DONE_REASON, BUDGET_STOP_REASON];
|
|
16169
|
+
var NO_AUTO_WAKE_REASONS = [EXITED_NO_DONE_REASON, BUDGET_STOP_REASON, ABORTED_STOP_REASON];
|
|
15887
16170
|
var REVIEW_BUDGET_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u0443\u043B\u043E\u0436\u0438\u043B\u043E\u0441\u044C \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442";
|
|
15888
16171
|
function reviewBudgetMark(cycle) {
|
|
15889
16172
|
return `${REVIEW_BUDGET_MARK} (\u043A\u0440\u0443\u0433 ${cycle})`;
|
|
@@ -16363,8 +16646,13 @@ function buildCiWakeNudge(summary) {
|
|
|
16363
16646
|
].join("\n");
|
|
16364
16647
|
}
|
|
16365
16648
|
function buildCiReadyNudge(prUrl, over) {
|
|
16366
|
-
const counts = over.kind
|
|
16367
|
-
const head = over.kind === "none" ? `\u2139\uFE0F \u0416\u0434\u0430\u0442\u044C CI \u043D\u0430 PR ${prUrl} \u041D\u0415\u0427\u0415\u0413\u041E: \u0447\u0435\u043A-\u0440\u0430\u043D\u043E\u0432 \u0443 PR \u043D\u0435\u0442, \u0438 \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0435\u0442 \u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E workflow \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A \u0442\u0443\u0442 \u043D\u0435 \u0431\u044B\u0432\u0430\u0435\u0442. \u0420\u043E\u0439 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u043B \u0442\u0435\u0431\u044F \u0434\u043E\u0434\u0435\u043B\u0430\u0442\u044C \u0440\u0435\u0432\u044C\u044E: \u0432\u044B\u043D\u043E\u0441\u0438 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E \u0442\u043E\u043C\u0443, \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0441\u0430\u043C (\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D), \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 CI \u0434\u0435\u0444\u0435\u043A\u0442\u043E\u043C PR \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0439 \u0438 \u0437\u0430\u0432\u043E\u0434\u0438\u0442\u044C \u0435\u0433\u043E \u043D\u0435 \u043F\u0440\u043E\u0441\u0438 \u2014 \u044D\u0442\u043E \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430 \u043F\u0440\u043E\u0435\u043A\u0442\u0430.` : over.kind === "gaveup" ? `\u26A0\uFE0F \u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 CI \u043D\u0430 PR ${prUrl} \u0442\u0430\u043A \u0438 \u041D\u0415 \u0417\u0410\u0412\u0415\u0420\u0428\u0418\u041B\u0418\u0421\u042C \u2014 CI \u043E\u0442\u0432\u0430\u043B\u0438\u043B\u0441\u044F \u0438\u043B\u0438 \u043D\u0430\u043C\u0435\u0440\u0442\u0432\u043E \u0432\u0438\u0441\u0438\u0442 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438, \u0438 \u0440\u043E\u0439 \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u043B \u0435\u0433\u043E \u043E\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044C (\u0441\u0440\u043E\u043A\u0438 \u0438 \u0441\u0447\u0451\u0442\u0447\u0438\u043A \u2014 \u0432 \u0441\u043E\u0431\u044B\u0442\u0438\u044F\u0445 \u043F\u0440\u043E\u0433\u043E\u043D\u0430). \u0420\u0430\u0437\u0431\u0443\u0434\u0438\u043B \u0442\u0435\u0431\u044F \u0434\u043E\u0432\u043E\u0434\u0438\u0442\u044C \u0440\u0435\u0432\u044C\u044E: \u0432\u044B\u043D\u043E\u0441\u0438 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E \u0442\u043E\u043C\u0443, \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0441\u0430\u043C (\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D). \u041D\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D\u043D\u044B\u0439 CI \u0434\u0435\u0444\u0435\u043A\u0442\u043E\u043C PR \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0439 \u0438 \u0447\u0438\u043D\u0438\u0442\u044C \u0438\u043D\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u043D\u0435 \u043F\u0440\u043E\u0441\u0438 \u2014 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u043F\u0440\u043E \u044D\u0442\u043E \u0440\u043E\u0439 \u0443\u0436\u0435 \u043F\u043E\u0437\u0432\u0430\u043B \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u043C \u0441\u0438\u0433\u043D\u0430\u043B\u043E\u043C.` :
|
|
16649
|
+
const counts = (over.kind === "pass" || over.kind === "none") && over.ci.total > 0 ? ` (${over.ci.pass}/${over.ci.total})` : "";
|
|
16650
|
+
const head = over.kind === "none" ? `\u2139\uFE0F \u0416\u0434\u0430\u0442\u044C CI \u043D\u0430 PR ${prUrl} \u041D\u0415\u0427\u0415\u0413\u041E: \u0447\u0435\u043A-\u0440\u0430\u043D\u043E\u0432 \u0443 PR \u043D\u0435\u0442, \u0438 \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0435\u0442 \u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E workflow \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A \u0442\u0443\u0442 \u043D\u0435 \u0431\u044B\u0432\u0430\u0435\u0442. \u0420\u043E\u0439 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u043B \u0442\u0435\u0431\u044F \u0434\u043E\u0434\u0435\u043B\u0430\u0442\u044C \u0440\u0435\u0432\u044C\u044E: \u0432\u044B\u043D\u043E\u0441\u0438 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E \u0442\u043E\u043C\u0443, \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0441\u0430\u043C (\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D), \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 CI \u0434\u0435\u0444\u0435\u043A\u0442\u043E\u043C PR \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0439 \u0438 \u0437\u0430\u0432\u043E\u0434\u0438\u0442\u044C \u0435\u0433\u043E \u043D\u0435 \u043F\u0440\u043E\u0441\u0438 \u2014 \u044D\u0442\u043E \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430 \u043F\u0440\u043E\u0435\u043A\u0442\u0430.` : over.kind === "gaveup" ? `\u26A0\uFE0F \u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 CI \u043D\u0430 PR ${prUrl} \u0442\u0430\u043A \u0438 \u041D\u0415 \u0417\u0410\u0412\u0415\u0420\u0428\u0418\u041B\u0418\u0421\u042C \u2014 CI \u043E\u0442\u0432\u0430\u043B\u0438\u043B\u0441\u044F \u0438\u043B\u0438 \u043D\u0430\u043C\u0435\u0440\u0442\u0432\u043E \u0432\u0438\u0441\u0438\u0442 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438, \u0438 \u0440\u043E\u0439 \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u043B \u0435\u0433\u043E \u043E\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044C (\u0441\u0440\u043E\u043A\u0438 \u0438 \u0441\u0447\u0451\u0442\u0447\u0438\u043A \u2014 \u0432 \u0441\u043E\u0431\u044B\u0442\u0438\u044F\u0445 \u043F\u0440\u043E\u0433\u043E\u043D\u0430). \u0420\u0430\u0437\u0431\u0443\u0434\u0438\u043B \u0442\u0435\u0431\u044F \u0434\u043E\u0432\u043E\u0434\u0438\u0442\u044C \u0440\u0435\u0432\u044C\u044E: \u0432\u044B\u043D\u043E\u0441\u0438 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E \u0442\u043E\u043C\u0443, \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0441\u0430\u043C (\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D). \u041D\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D\u043D\u044B\u0439 CI \u0434\u0435\u0444\u0435\u043A\u0442\u043E\u043C PR \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0439 \u0438 \u0447\u0438\u043D\u0438\u0442\u044C \u0438\u043D\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u043D\u0435 \u043F\u0440\u043E\u0441\u0438 \u2014 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u043F\u0440\u043E \u044D\u0442\u043E \u0440\u043E\u0439 \u0443\u0436\u0435 \u043F\u043E\u0437\u0432\u0430\u043B \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u043C \u0441\u0438\u0433\u043D\u0430\u043B\u043E\u043C.` : (
|
|
16651
|
+
// TASWM-483: чеки, скорее всего, ЕСТЬ и даже зелёные — их просто не видно рою (403).
|
|
16652
|
+
// Врать «CI не прошёл» тут так же нельзя, как «CI зелёный»: единственная честная
|
|
16653
|
+
// формулировка — «не проверялся», и она обязана доехать до вердикта.
|
|
16654
|
+
over.kind === "denied" ? `\u26A0\uFE0F \u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 CI \u043D\u0430 PR ${prUrl} \u0440\u043E\u044E \u041D\u0415 \u0412\u0418\u0414\u041D\u042B: GitHub \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043E\u0442\u043A\u0430\u0437\u043E\u043C \u043F\u043E \u043F\u0440\u0430\u0432\u0430\u043C \u2014 403${over.detail ? ` (${over.detail})` : ""}. \u0427\u0435\u043A\u0438 \u043D\u0430 PR, \u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E, \u0435\u0441\u0442\u044C \u0438 \u0434\u0430\u0436\u0435 \u0437\u0435\u043B\u0451\u043D\u044B\u0435, \u043D\u043E \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C \u0438\u0445 \u0440\u043E\u0439 \u043D\u0435 \u043C\u043E\u0436\u0435\u0442, \u043F\u043E\u043A\u0430 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u043D\u0435 \u0432\u044B\u0434\u0430\u0441\u0442 \u0434\u043E\u0441\u0442\u0443\u043F; \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u0440\u043E\u0439 \u0443\u0436\u0435 \u043F\u043E\u0437\u0432\u0430\u043B \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u043C \u0441\u0438\u0433\u043D\u0430\u043B\u043E\u043C. \u0420\u0430\u0437\u0431\u0443\u0434\u0438\u043B \u0442\u0435\u0431\u044F \u0434\u043E\u0432\u043E\u0434\u0438\u0442\u044C \u0440\u0435\u0432\u044C\u044E: \u0432\u044B\u043D\u043E\u0441\u0438 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E \u0442\u043E\u043C\u0443, \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0441\u0430\u043C (\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D), \u0438 \u041E\u0411\u042F\u0417\u0410\u0422\u0415\u041B\u042C\u041D\u041E \u043D\u0430\u043F\u0438\u0448\u0438 \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0435 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u043E\u0439 \xABCI \u043D\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u043B\u0441\u044F: \u0440\u043E\u044E \u043D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043A \u0447\u0435\u043A\u0430\u043C (403)\xBB. \u041E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u0434\u0435\u0444\u0435\u043A\u0442\u043E\u043C PR \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0439 \u0438 \u0447\u0438\u043D\u0438\u0442\u044C \u0435\u0433\u043E \u043D\u0435 \u043F\u0440\u043E\u0441\u0438.` : `\u2705 \u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 CI \u043D\u0430 PR ${prUrl} \u0417\u0410\u0412\u0415\u0420\u0428\u0418\u041B\u0418\u0421\u042C \u0438 \u043E\u043D\u0438 \u0437\u0435\u043B\u0451\u043D\u044B\u0435${counts} \u2014 \u0440\u043E\u0439 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u043B \u0442\u0435\u0431\u044F \u0434\u043E\u0434\u0435\u043B\u0430\u0442\u044C \u0440\u0435\u0432\u044C\u044E, \u0436\u0434\u0430\u0442\u044C \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u0447\u0435\u0433\u043E.`
|
|
16655
|
+
);
|
|
16368
16656
|
return [
|
|
16369
16657
|
head,
|
|
16370
16658
|
"",
|
|
@@ -16671,6 +16959,11 @@ function droneLabel(run) {
|
|
|
16671
16959
|
function signalTitle(run, what) {
|
|
16672
16960
|
return `${run.task_slug || droneLabel(run)}: ${what}`;
|
|
16673
16961
|
}
|
|
16962
|
+
var tickNoise = makeRepeatFilter();
|
|
16963
|
+
function warnRepeating(key, message, now) {
|
|
16964
|
+
const line = tickNoise.say(key, message, now);
|
|
16965
|
+
if (line) console.error(line);
|
|
16966
|
+
}
|
|
16674
16967
|
function narrate(deps, text) {
|
|
16675
16968
|
try {
|
|
16676
16969
|
deps.narrate?.(text);
|
|
@@ -16834,6 +17127,27 @@ async function handleCi(deps, run, now, worktreeExists, hasTranscript, droneCap)
|
|
|
16834
17127
|
return;
|
|
16835
17128
|
}
|
|
16836
17129
|
const ci = await deps.ci.checks(run.pr_url).catch(() => null);
|
|
17130
|
+
if (ci?.bucket === "denied") {
|
|
17131
|
+
const seen = ciDeniedStreak(deps.db, run.id) + 1;
|
|
17132
|
+
if (seen < CI_DENIED_LIMIT) {
|
|
17133
|
+
deps.db.addEvent(
|
|
17134
|
+
run.id,
|
|
17135
|
+
"ci_checked",
|
|
17136
|
+
`CI \u043D\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D: ${CI_DENIED_MARK}${ci.detail ? ` \u2014 ${ci.detail}` : ""}; \u043F\u043E\u043F\u044B\u0442\u043A\u0430 ${seen}/${CI_DENIED_LIMIT} [${CI_WAIT_POLL_MARK}]`
|
|
17137
|
+
);
|
|
17138
|
+
return;
|
|
17139
|
+
}
|
|
17140
|
+
closeCiWatch(
|
|
17141
|
+
deps,
|
|
17142
|
+
run,
|
|
17143
|
+
CI_DENIED_STOP_MARK,
|
|
17144
|
+
`GitHub \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 403 \u043D\u0430 \u0447\u0442\u0435\u043D\u0438\u0435 \u0447\u0435\u043A-\u0440\u0430\u043D\u043E\u0432${ci.detail ? ` (${ci.detail})` : ""} \u2014 ${seen} \u043E\u043F\u0440\u043E\u0441(\u043E\u0432) \u043F\u043E\u0434\u0440\u044F\u0434. \u042D\u0442\u043E \u043D\u0435 \u0441\u0431\u043E\u0439 \u0441\u0435\u0442\u0438 \u0438 \u043D\u0435 \u043C\u0435\u0434\u043B\u0435\u043D\u043D\u044B\u0439 CI: \u0443 \u0440\u043E\u044F \u043D\u0435\u0442 \u043F\u0440\u0430\u0432 \u043D\u0430 \u0447\u0435\u043A\u0438 \u0432 \u044D\u0442\u043E\u043C \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438, \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0442\u044C \u0431\u0435\u0441\u0441\u043C\u044B\u0441\u043B\u0435\u043D\u043D\u043E. \u041F\u0440\u043E\u0432\u0435\u0440\u043E\u043A \u044F \u043D\u0435 \u0432\u0438\u0436\u0443 \u2014 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0441\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u044E; \u0440\u0435\u0432\u044C\u044E \u043F\u043E\u0439\u0434\u0451\u0442 \u0411\u0415\u0417 CI (\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u043F\u043E\u043C\u0435\u0442\u0438\u0442 \u044D\u0442\u043E \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0435), \u0434\u043E\u0441\u0442\u0443\u043F \u0432\u044B\u0434\u0430\u0451\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A: \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C GitHub App \u0440\u043E\u044F \u043D\u0430 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439/\u043E\u0440\u0433\u0430\u043D\u0438\u0437\u0430\u0446\u0438\u044E.`,
|
|
17145
|
+
`${droneLabel(run)}: GitHub \u043D\u0435 \u0434\u0430\u0451\u0442 \u0440\u043E\u044E \u0447\u0438\u0442\u0430\u0442\u044C \u0447\u0435\u043A\u0438 (403) \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A \u043D\u0435 \u0432\u0438\u0436\u0443, \u0437\u043E\u0432\u0443 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430 \u0438 \u0432\u0435\u0434\u0443 \u0440\u0435\u0432\u044C\u044E \u0431\u0435\u0437 CI.`
|
|
17146
|
+
);
|
|
17147
|
+
await escalateCiDenied(deps, run, ci.detail ?? null, seen);
|
|
17148
|
+
await wakeReviewerOnCiReady(deps, run, { kind: "denied", ...ci.detail ? { detail: ci.detail } : {} });
|
|
17149
|
+
return;
|
|
17150
|
+
}
|
|
16837
17151
|
if (ci?.bucket === "none" && !repoHasCi(deps, run)) {
|
|
16838
17152
|
closeCiWatch(
|
|
16839
17153
|
deps,
|
|
@@ -16877,7 +17191,27 @@ function ciWatchMark(base3, cycle) {
|
|
|
16877
17191
|
return `${base3} [\u0446\u0438\u043A\u043B ${cycle}]`;
|
|
16878
17192
|
}
|
|
16879
17193
|
function isCiWatchClose(text) {
|
|
16880
|
-
return text.includes(CI_NO_CHECKS_MARK) || text.includes(CI_POLL_LIMIT_MARK);
|
|
17194
|
+
return text.includes(CI_NO_CHECKS_MARK) || text.includes(CI_POLL_LIMIT_MARK) || text.includes(CI_DENIED_STOP_MARK);
|
|
17195
|
+
}
|
|
17196
|
+
function ciDeniedStreak(db, runId) {
|
|
17197
|
+
return ciStreak(db, runId, CI_DENIED_MARK).count;
|
|
17198
|
+
}
|
|
17199
|
+
async function escalateCiDenied(deps, run, detail, polls) {
|
|
17200
|
+
const why = `${CI_DENIED_MARK}: GitHub \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 403 \u043D\u0430 \u0447\u0442\u0435\u043D\u0438\u0435 \u0447\u0435\u043A-\u0440\u0430\u043D\u043E\u0432 PR${detail ? ` (${detail})` : ""} \u2014 ${polls} \u043E\u043F\u0440\u043E\u0441(\u043E\u0432) \u043F\u043E\u0434\u0440\u044F\u0434. \u0420\u043E\u0439 \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0442\u044C PR \u0432 \u044D\u0442\u043E\u043C \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0443\u043C\u0435\u0435\u0442, \u0430 \u0447\u0438\u0442\u0430\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u2014 \u043D\u0435\u0442: \u043F\u0440\u0430\u0432\u0430 \u0432\u044B\u0434\u0430\u043D\u044B \u0447\u0430\u0441\u0442\u0438\u0447\u043D\u043E. \u041E\u043F\u0440\u043E\u0441 CI \u043F\u043E \u044D\u0442\u043E\u043C\u0443 PR \u043F\u0440\u0435\u043A\u0440\u0430\u0449\u0451\u043D, \u0440\u0435\u0432\u044C\u044E \u0438\u0434\u0451\u0442 \u0431\u0435\u0437 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A. \u041D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A: \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C GitHub App \u0440\u043E\u044F \u043D\u0430 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439/\u043E\u0440\u0433\u0430\u043D\u0438\u0437\u0430\u0446\u0438\u044E.`;
|
|
17201
|
+
deps.db.addEvent(run.id, "anomaly", why);
|
|
17202
|
+
if (!deps.watchdog?.notify) return;
|
|
17203
|
+
const signal = {
|
|
17204
|
+
key: `ci_denied:${run.id}:${run.review_cycle}`,
|
|
17205
|
+
title: signalTitle(run, "GitHub \u043D\u0435 \u0434\u0430\u0451\u0442 \u0440\u043E\u044E \u0447\u0438\u0442\u0430\u0442\u044C \u0447\u0435\u043A\u0438 (403)"),
|
|
17206
|
+
body: `${run.task_slug}: ${polls} \u043E\u043F\u0440\u043E\u0441(\u043E\u0432) \u043F\u043E\u0434\u0440\u044F\u0434 \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B\u0438\u0441\u044C 403${detail ? ` (${detail})` : ""} \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A \u0440\u043E\u0439 \u043D\u0435 \u0432\u0438\u0434\u0438\u0442 \u0438 \u0436\u0434\u0430\u0442\u044C \u0438\u0445 \u043D\u0435 \u0431\u0443\u0434\u0435\u0442. \u0420\u0435\u0432\u044C\u044E \u043F\u043E\u0439\u0434\u0451\u0442 \u0431\u0435\u0437 CI (\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u043F\u043E\u043C\u0435\u0442\u0438\u0442 \u044D\u0442\u043E \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0435). \u0412\u044B\u0434\u0430\u0439 \u0434\u043E\u0441\u0442\u0443\u043F: GitHub App \u0440\u043E\u044F \u043D\u0430 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439/\u043E\u0440\u0433\u0430\u043D\u0438\u0437\u0430\u0446\u0438\u044E. PR: ${run.pr_url ?? "\u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043D"}.`,
|
|
17207
|
+
runId: run.id,
|
|
17208
|
+
...hrefOf(run.task_url)
|
|
17209
|
+
};
|
|
17210
|
+
try {
|
|
17211
|
+
await deps.watchdog.notify(signal);
|
|
17212
|
+
} catch (e) {
|
|
17213
|
+
console.error(`[overmind] \u0441\u0438\u0433\u043D\u0430\u043B \u043E\u0431 \u043E\u0442\u043A\u0430\u0437\u0435 GitHub \u043F\u043E \u0447\u0435\u043A\u0430\u043C \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${run.id} \u043D\u0435 \u0434\u043E\u043B\u0435\u0442\u0435\u043B:`, e);
|
|
17214
|
+
}
|
|
16881
17215
|
}
|
|
16882
17216
|
function ciWatchClosed(db, run) {
|
|
16883
17217
|
return ciWatchCloseKind(db, run) != null;
|
|
@@ -16886,20 +17220,31 @@ var NO_CHECKS_STATUS = { bucket: "none", failing: [], failingRunIds: [], total:
|
|
|
16886
17220
|
function ciWatchCloseKind(db, run) {
|
|
16887
17221
|
const noChecks = ciWatchMark(CI_NO_CHECKS_MARK, run.review_cycle);
|
|
16888
17222
|
const gaveUp = ciWatchMark(CI_POLL_LIMIT_MARK, run.review_cycle);
|
|
17223
|
+
const denied = ciWatchMark(CI_DENIED_STOP_MARK, run.review_cycle);
|
|
16889
17224
|
for (const e of db.events(run.id)) {
|
|
16890
17225
|
const text = e.text ?? "";
|
|
16891
17226
|
if (text.includes(noChecks)) return { kind: "none", ci: NO_CHECKS_STATUS };
|
|
16892
17227
|
if (text.includes(gaveUp)) return { kind: "gaveup" };
|
|
17228
|
+
if (text.includes(denied)) {
|
|
17229
|
+
const detail = deniedDetailFrom(text);
|
|
17230
|
+
return { kind: "denied", ...detail ? { detail } : {} };
|
|
17231
|
+
}
|
|
16893
17232
|
}
|
|
16894
17233
|
return null;
|
|
16895
17234
|
}
|
|
17235
|
+
function deniedDetailFrom(text) {
|
|
17236
|
+
return text.match(/на чтение чек-ранов \(([^)]+)\)/)?.[1];
|
|
17237
|
+
}
|
|
16896
17238
|
function ciPollStreak(db, runId) {
|
|
17239
|
+
return ciStreak(db, runId, CI_WAIT_POLL_MARK);
|
|
17240
|
+
}
|
|
17241
|
+
function ciStreak(db, runId, mark) {
|
|
16897
17242
|
const evs = db.events(runId).filter((e) => e.kind === "ci_wake" || e.kind === "ci_checked" || isCiWatchClose(e.text ?? ""));
|
|
16898
17243
|
let count = 0;
|
|
16899
17244
|
let sinceMs = null;
|
|
16900
17245
|
for (let i = evs.length - 1; i >= 0; i -= 1) {
|
|
16901
17246
|
const e = evs[i];
|
|
16902
|
-
if (e.kind !== "ci_checked" || !(e.text ?? "").includes(
|
|
17247
|
+
if (e.kind !== "ci_checked" || !(e.text ?? "").includes(mark)) break;
|
|
16903
17248
|
count += 1;
|
|
16904
17249
|
sinceMs = sqliteMs(e.at);
|
|
16905
17250
|
}
|
|
@@ -16954,6 +17299,10 @@ function ciReadyLabel(over) {
|
|
|
16954
17299
|
// (isCiWatchClose/ciWatchCloseKind ищут его по подстроке), и событие о том, что
|
|
16955
17300
|
// побудка ждёт слота, не должно выглядеть вторым решением о закрытии.
|
|
16956
17301
|
"\u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A \u0442\u0430\u043A \u0438 \u043D\u0435 \u0434\u043E\u0436\u0434\u0430\u043B\u0438\u0441\u044C, \u0440\u043E\u0439 \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u043B \u043E\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044C CI"
|
|
17302
|
+
) : over.kind === "denied" ? (
|
|
17303
|
+
// По той же причине не дословно CI_DENIED_STOP_MARK (TASWM-483) — но «403» в тексте
|
|
17304
|
+
// остаётся: по журналу должна быть видна причина, а не «проверок нет».
|
|
17305
|
+
"\u0447\u0435\u043A\u0438 \u0440\u043E\u044E \u043D\u0435 \u0432\u0438\u0434\u043D\u044B: GitHub \u043E\u0442\u043A\u0430\u0437\u0430\u043B \u043F\u043E \u043F\u0440\u0430\u0432\u0430\u043C (403)"
|
|
16957
17306
|
) : `CI \u0437\u0435\u043B\u0451\u043D\u044B\u0439 (${over.ci.pass}/${over.ci.total})`;
|
|
16958
17307
|
}
|
|
16959
17308
|
async function wakeReviewerOnCiReady(deps, run, over) {
|
|
@@ -17386,14 +17735,17 @@ async function trySpawnQueuedRun(deps, run) {
|
|
|
17386
17735
|
return await failBeforeStart(deps, run, String(e));
|
|
17387
17736
|
}
|
|
17388
17737
|
}
|
|
17738
|
+
function holderStillHolds(deps, holder, now) {
|
|
17739
|
+
if (holder.pid != null) return deps.isAlive(holder.pid);
|
|
17740
|
+
return now - lastActivityMs(deps.db, holder) < DEAD_HOLDER_SILENCE_MS;
|
|
17741
|
+
}
|
|
17389
17742
|
async function healBusyWorktree(deps, run, e) {
|
|
17390
17743
|
const path = e.worktreePath;
|
|
17391
17744
|
if (!deps.releaseWorktree) return false;
|
|
17392
17745
|
if (deps.liveWorktrees?.().has(path)) return false;
|
|
17746
|
+
const now = deps.now ? deps.now() : Date.now();
|
|
17393
17747
|
const holder = deps.db.listRuns().find((r) => r.id !== run.id && r.worktree_path === path && !isTerminalStatus(r.status));
|
|
17394
|
-
if (holder && (holder
|
|
17395
|
-
return false;
|
|
17396
|
-
}
|
|
17748
|
+
if (holder && holderStillHolds(deps, holder, now)) return false;
|
|
17397
17749
|
if (deps.isDirty && await deps.isDirty(path).catch(() => true)) {
|
|
17398
17750
|
deps.db.addEvent(
|
|
17399
17751
|
run.id,
|
|
@@ -17403,10 +17755,12 @@ async function healBusyWorktree(deps, run, e) {
|
|
|
17403
17755
|
narrate(deps, `${run.task_slug}: worktree \u0437\u0430\u043D\u044F\u0442 \u043C\u0451\u0440\u0442\u0432\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0435\u0439, \u043D\u043E \u0432 \u043D\u0451\u043C \u043D\u0435\u0441\u0434\u0430\u043D\u043D\u0430\u044F \u0440\u0430\u0431\u043E\u0442\u0430 \u2014 \u043D\u0435 \u0442\u0440\u043E\u0433\u0430\u044E, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A.`);
|
|
17404
17756
|
return false;
|
|
17405
17757
|
}
|
|
17758
|
+
const silence = holder && holder.pid == null ? `\u043C\u043E\u043B\u0447\u0430\u043B ${humanMinutes(now - lastActivityMs(deps.db, holder))}` : null;
|
|
17759
|
+
const why = silence ? `\u0434\u0435\u0440\u0436\u0430\u0442\u0435\u043B\u044C #${holder.id} (${holder.status}) ${silence} \u0438 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430 \u043D\u0435 \u0438\u043C\u0435\u0435\u0442 \u2014 \u043D\u0435 \u0436\u0434\u0451\u0442 \u043C\u0435\u0440\u0436\u0430, \u0430 \u0443\u043C\u0435\u0440` : "\u0436\u0438\u0432\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u0437\u0430 \u043D\u0438\u043C \u043D\u0435\u0442";
|
|
17406
17760
|
await deps.releaseWorktree(run.repo, path).catch(() => {
|
|
17407
17761
|
});
|
|
17408
|
-
deps.db.addEvent(run.id, "step", `worktree ${path} \u0434\u0435\u0440\u0436\u0430\u043B\u0430 \u043C\u0451\u0440\u0442\u0432\u0430\u044F \u0441\u0435\u0441\u0441\u0438\u044F \u2014 \u043F\u0440\u0438\u0431\u0440\u0430\u043B \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u044E \u0441\u043F\u0430\u0432\u043D (TASWM-476)`);
|
|
17409
|
-
if (holder) deps.db.addEvent(holder.id, "step", `worktree ${path} \u043E\u0441\u0432\u043E\u0431\u043E\u0436\u0434\u0451\u043D:
|
|
17762
|
+
deps.db.addEvent(run.id, "step", `worktree ${path} \u0434\u0435\u0440\u0436\u0430\u043B\u0430 \u043C\u0451\u0440\u0442\u0432\u0430\u044F \u0441\u0435\u0441\u0441\u0438\u044F (${why}) \u2014 \u043F\u0440\u0438\u0431\u0440\u0430\u043B \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u044E \u0441\u043F\u0430\u0432\u043D (TASWM-476)`);
|
|
17763
|
+
if (holder) deps.db.addEvent(holder.id, "step", `worktree ${path} \u043E\u0441\u0432\u043E\u0431\u043E\u0436\u0434\u0451\u043D: ${why}, \u0441\u043B\u0430\u0433 \u043F\u043E\u043D\u0430\u0434\u043E\u0431\u0438\u043B\u0441\u044F \u043F\u0440\u043E\u0433\u043E\u043D\u0443 #${run.id}`);
|
|
17410
17764
|
return true;
|
|
17411
17765
|
}
|
|
17412
17766
|
async function failBeforeStart(deps, run, reason) {
|
|
@@ -17717,6 +18071,15 @@ async function buildReliefCostCandidatesFor(deps, now, droneCap) {
|
|
|
17717
18071
|
...hrefOf(worstRun?.task_url)
|
|
17718
18072
|
}));
|
|
17719
18073
|
}
|
|
18074
|
+
async function workFate(deps, run) {
|
|
18075
|
+
if (run.pr_url) return `PR \u043E\u0442\u043A\u0440\u044B\u0442: ${run.pr_url}`;
|
|
18076
|
+
if (!deps.branchOnOrigin) return "";
|
|
18077
|
+
const branch = run.branch || run.task_slug;
|
|
18078
|
+
const exists = await deps.branchOnOrigin(run.repo, branch).catch(() => null);
|
|
18079
|
+
if (exists === true) return `${EXIT_WORK_PUSHED} (\u0432\u0435\u0442\u043A\u0430 ${branch})`;
|
|
18080
|
+
if (exists === false) return `${EXIT_WORK_LOST} (\u0432\u0435\u0442\u043A\u0438 ${branch} \u043D\u0430 origin \u043D\u0435\u0442)`;
|
|
18081
|
+
return EXIT_WORK_UNKNOWN;
|
|
18082
|
+
}
|
|
17720
18083
|
async function deliveredPrUrl(deps, run) {
|
|
17721
18084
|
if (run.pr_url) return run.pr_url;
|
|
17722
18085
|
if (!deps.openPrsForBranch) return null;
|
|
@@ -18108,7 +18471,11 @@ async function tickOnce(deps) {
|
|
|
18108
18471
|
deps.db.addEvent(run.id, "step", `${SELF_REFERENCE_MARK}: PR \u0442\u0440\u043E\u0433\u0430\u0435\u0442 ${DAEMON_CODE_PATHS.join(", ")} \u2014 \u044D\u0441\u043A\u0430\u043B\u0438\u0440\u043E\u0432\u0430\u043D\u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443, \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E`);
|
|
18109
18472
|
narrate(deps, `PR ${droneLabel(run)} \u0442\u0440\u043E\u0433\u0430\u0435\u0442 \u043A\u043E\u0434 \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0441\u0430\u043C \u0441\u0435\u0431\u044F \u043D\u0435 \u0430\u0442\u0442\u0435\u0441\u0442\u0443\u0435\u0442, \u044D\u0441\u043A\u0430\u043B\u0438\u0440\u0443\u044E \u0438 \u043D\u0435 \u0433\u043E\u043D\u044F\u044E \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E.`);
|
|
18110
18473
|
} catch (e) {
|
|
18111
|
-
|
|
18474
|
+
warnRepeating(
|
|
18475
|
+
`self-reference:${signal.key}`,
|
|
18476
|
+
`[overmind] \u0441\u0442\u043E\u0440\u043E\u0436: self-reference \u043D\u043E\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F \xAB${signal.key}\xBB \u043D\u0435 \u0434\u043E\u043B\u0435\u0442\u0435\u043B\u0430, \u043F\u043E\u043F\u0440\u043E\u0431\u0443\u044E \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0442\u0438\u043A\u043E\u043C: ${describeError(e)}`,
|
|
18477
|
+
now
|
|
18478
|
+
);
|
|
18112
18479
|
}
|
|
18113
18480
|
}
|
|
18114
18481
|
continue;
|
|
@@ -18314,7 +18681,10 @@ async function tickOnce(deps) {
|
|
|
18314
18681
|
continue;
|
|
18315
18682
|
}
|
|
18316
18683
|
}
|
|
18317
|
-
if (!alive && outcome === "graceful" && run.status === "running" && run.resolves_run_id == null && run.reviews_run_id == null && run.verifies_run_id == null && run.kind !== "hero") {
|
|
18684
|
+
if (!alive && (outcome === "graceful" || outcome === "aborted") && run.status === "running" && run.resolves_run_id == null && run.reviews_run_id == null && run.verifies_run_id == null && run.kind !== "hero") {
|
|
18685
|
+
const aborted = wasAborted(outcome);
|
|
18686
|
+
const abort = aborted ? deps.exitAbort?.(run) ?? null : null;
|
|
18687
|
+
const what = aborted ? abortReason(abort) : EXITED_NO_DONE_REASON;
|
|
18318
18688
|
const wt = run.worktree_path;
|
|
18319
18689
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(wt);
|
|
18320
18690
|
if (deps.respawn && canContinue && run.respawns < RESPAWN_LIMIT) {
|
|
@@ -18322,21 +18692,31 @@ async function tickOnce(deps) {
|
|
|
18322
18692
|
deps.db.addEvent(
|
|
18323
18693
|
run.id,
|
|
18324
18694
|
"respawned",
|
|
18325
|
-
`\u0430\u0432\u0442\u043E-\u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u0435 #${n}: \u0441\u0435\u0441\u0441\u0438\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0430\u0441\u044C \u0441\u0430\u043C\u0430 \u0431\u0435\u0437 done() \u2014 --continue, \u0434\u043E\u0432\u043E\u0436\u0443 \u0434\u043E \u0441\u0434\u0430\u0447\u0438 (\u043D\u0435 \u043F\u0435\u0440\u0435\u0434\u0435\u043B\u044B\u0432\u0430\u044E \u0441\u0434\u0435\u043B\u0430\u043D\u043D\u043E\u0435)`
|
|
18695
|
+
aborted ? `\u0430\u0432\u0442\u043E-\u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u0435 #${n}: ${what} \u2014 --continue \u0441 \u043D\u0430\u043A\u0430\u0437\u043E\u043C \u043D\u0435 \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0442\u044C \u0442\u044F\u0436\u0451\u043B\u0443\u044E \u043A\u043E\u043C\u0430\u043D\u0434\u0443` : `\u0430\u0432\u0442\u043E-\u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u0435 #${n}: \u0441\u0435\u0441\u0441\u0438\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0430\u0441\u044C \u0441\u0430\u043C\u0430 \u0431\u0435\u0437 done() \u2014 --continue, \u0434\u043E\u0432\u043E\u0436\u0443 \u0434\u043E \u0441\u0434\u0430\u0447\u0438 (\u043D\u0435 \u043F\u0435\u0440\u0435\u0434\u0435\u043B\u044B\u0432\u0430\u044E \u0441\u0434\u0435\u043B\u0430\u043D\u043D\u043E\u0435)`
|
|
18696
|
+
);
|
|
18697
|
+
narrate(
|
|
18698
|
+
deps,
|
|
18699
|
+
aborted ? `${droneLabel(run)}: ${what}. \u041F\u043E\u0434\u043D\u0438\u043C\u0430\u044E --continue.` : `${droneLabel(run)} \u0432\u044B\u0448\u0435\u043B \u0441\u0430\u043C, \u043D\u0435 \u043F\u043E\u0437\u0432\u0430\u0432 done() \u2014 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E --continue, \u0447\u0442\u043E\u0431\u044B \u0434\u043E\u0432\u0451\u043B \u0434\u043E PR/done.`
|
|
18326
18700
|
);
|
|
18327
|
-
narrate(deps, `${droneLabel(run)} \u0432\u044B\u0448\u0435\u043B \u0441\u0430\u043C, \u043D\u0435 \u043F\u043E\u0437\u0432\u0430\u0432 done() \u2014 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E --continue, \u0447\u0442\u043E\u0431\u044B \u0434\u043E\u0432\u0451\u043B \u0434\u043E PR/done.`);
|
|
18328
18701
|
try {
|
|
18329
|
-
await deps.respawn(run, "continue", EXITED_CONTINUE_NUDGE);
|
|
18702
|
+
await deps.respawn(run, "continue", aborted ? ABORTED_CONTINUE_NUDGE : EXITED_CONTINUE_NUDGE);
|
|
18330
18703
|
} catch (e) {
|
|
18331
|
-
|
|
18332
|
-
deps.db.
|
|
18704
|
+
const failed = `${aborted ? ABORTED_STOP_REASON : EXITED_NO_DONE_REASON}; \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u0435 \u0441\u043E\u0440\u0432\u0430\u043B\u043E\u0441\u044C: ${String(e)}`;
|
|
18705
|
+
deps.db.updateRun(run.id, { status: "paused", stop_reason: failed });
|
|
18706
|
+
deps.db.addEvent(run.id, "paused", failed);
|
|
18333
18707
|
}
|
|
18334
18708
|
continue;
|
|
18335
18709
|
}
|
|
18336
|
-
const
|
|
18710
|
+
const marker = aborted ? ABORTED_STOP_REASON : EXITED_NO_DONE_REASON;
|
|
18711
|
+
const head = run.respawns >= RESPAWN_LIMIT ? `${marker} \u2014 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u043B\u0438\u043C\u0438\u0442 \u0430\u0432\u0442\u043E-\u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u0439 (${RESPAWN_LIMIT}), \u043D\u0443\u0436\u0435\u043D \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440` : marker;
|
|
18712
|
+
const fate = await workFate(deps, run);
|
|
18713
|
+
const reason2 = [head, aborted ? what : null, fate].filter(Boolean).join("; ");
|
|
18337
18714
|
deps.db.updateRun(run.id, { status: "paused", stop_reason: reason2 });
|
|
18338
18715
|
deps.db.addEvent(run.id, "paused", reason2);
|
|
18339
|
-
narrate(
|
|
18716
|
+
narrate(
|
|
18717
|
+
deps,
|
|
18718
|
+
aborted ? `${droneLabel(run)}: ${what}. \u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u043C \u2014 \xAB\u0437\u0430\u043C\u0435\u0440\xBB, ${fate || "\u0441\u0443\u0434\u044C\u0431\u0430 \u0440\u0430\u0431\u043E\u0442\u044B \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430"}.` : `${droneLabel(run)} \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B \u0441\u0435\u0441\u0441\u0438\u044E \u0441\u0430\u043C, \u043D\u0435 \u043F\u043E\u0437\u0432\u0430\u0432 done() \u2014 \u0441\u0442\u0430\u0442\u0443\u0441 \xAB\u0437\u0430\u043C\u0435\u0440\xBB, \u043F\u0440\u0438\u0447\u0438\u043D\u0430 \u0432 \u043B\u043E\u0433\u0435 (TASWM-229).`
|
|
18719
|
+
);
|
|
18340
18720
|
continue;
|
|
18341
18721
|
}
|
|
18342
18722
|
if (!alive && run.status === "running" && run.resolves_run_id == null && run.reviews_run_id == null && run.verifies_run_id == null && deps.respawn && run.respawns < RESPAWN_LIMIT) {
|
|
@@ -18384,8 +18764,8 @@ async function tickOnce(deps) {
|
|
|
18384
18764
|
const ciWatchOff = parent != null && ciWatchClosed(deps.db, parent);
|
|
18385
18765
|
const ci = ciWatchOff ? null : await deps.ci.checks(run.pr_url).catch(() => null);
|
|
18386
18766
|
const noCiRepo = ci?.bucket === "none" && !repoHasCi(deps, run);
|
|
18387
|
-
if (!ciWatchOff && !noCiRepo && (ci == null || ci.bucket === "pending" || ci.bucket === "none")) {
|
|
18388
|
-
const what = ci == null ? "gh \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043F\u0440\u043E \u0447\u0435\u043A\u0438 \u2014 \u0436\u0434\u0443, \u043F\u043E\u043A\u0430 \u043F\u0440\u043E\u044F\u0441\u043D\u0438\u0442\u0441\u044F" : ci.bucket === "none" ? "\u0447\u0435\u043A\u043E\u0432 \u043D\u0430 PR \u043F\u043E\u043A\u0430 \u043D\u0435 \u0432\u0438\u0434\u043D\u043E \u2014 \u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u0447\u0435\u043A-\u0440\u0430\u043D\u0430, \u043D\u043E workflow \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0435\u0441\u0442\u044C" : `CI ${ci.pass}/${ci.total}`;
|
|
18767
|
+
if (!ciWatchOff && !noCiRepo && (ci == null || ci.bucket === "pending" || ci.bucket === "none" || ci.bucket === "denied")) {
|
|
18768
|
+
const what = ci == null ? "gh \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043F\u0440\u043E \u0447\u0435\u043A\u0438 \u2014 \u0436\u0434\u0443, \u043F\u043E\u043A\u0430 \u043F\u0440\u043E\u044F\u0441\u043D\u0438\u0442\u0441\u044F" : ci.bucket === "none" ? "\u0447\u0435\u043A\u043E\u0432 \u043D\u0430 PR \u043F\u043E\u043A\u0430 \u043D\u0435 \u0432\u0438\u0434\u043D\u043E \u2014 \u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u0447\u0435\u043A-\u0440\u0430\u043D\u0430, \u043D\u043E workflow \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0435\u0441\u0442\u044C" : ci.bucket === "denied" ? `\u0447\u0435\u043A\u0438 \u0440\u043E\u044E \u043D\u0435 \u0432\u0438\u0434\u043D\u044B: GitHub \u043E\u0442\u043A\u0430\u0437\u0430\u043B \u043F\u043E \u043F\u0440\u0430\u0432\u0430\u043C (403)${ci.detail ? ` \u2014 ${ci.detail}` : ""}` : `CI ${ci.pass}/${ci.total}`;
|
|
18389
18769
|
deps.db.updateRun(run.id, { status: "paused", stop_reason: AWAITING_CI_REASON });
|
|
18390
18770
|
deps.db.addEvent(run.id, "paused", `${AWAITING_CI_REASON} (${what}) \u2014 \u043D\u0435 \u0441\u043C\u0435\u0440\u0442\u044C: \u0440\u0430\u0437\u0431\u0443\u0436\u0443 \u0441\u0430\u043C, \u043A\u043E\u0433\u0434\u0430 \u0447\u0435\u043A\u0438 \u0434\u043E\u0439\u0434\u0443\u0442 \u0434\u043E \u043A\u043E\u043D\u0446\u0430`);
|
|
18391
18771
|
narrate(deps, `${droneLabel(run)} \u0443\u0448\u0451\u043B \u0436\u0434\u0430\u0442\u044C CI (${what}) \u2014 \u043D\u0435 \u0445\u043E\u0440\u043E\u043D\u044E, \u0440\u0430\u0437\u0431\u0443\u0436\u0443 \u043F\u043E \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A.`);
|
|
@@ -18512,8 +18892,13 @@ async function tickOnce(deps) {
|
|
|
18512
18892
|
const signal = finalCall ? { ...item.signal, body: `${item.signal.body} ${LAST_REMINDER_SUFFIX}` } : item.signal;
|
|
18513
18893
|
try {
|
|
18514
18894
|
await az.notify(signal);
|
|
18895
|
+
tickNoise.forget(`notify:${signal.key}`);
|
|
18515
18896
|
} catch (e) {
|
|
18516
|
-
|
|
18897
|
+
warnRepeating(
|
|
18898
|
+
`notify:${signal.key}`,
|
|
18899
|
+
`[overmind] \u043D\u0430\u0434\u0437\u043E\u0440: \u043D\u043E\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F \xAB${signal.key}\xBB \u043D\u0435 \u0434\u043E\u043B\u0435\u0442\u0435\u043B\u0430, \u043F\u043E\u043F\u0440\u043E\u0431\u0443\u044E \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0442\u0438\u043A\u043E\u043C: ${describeError(e)}`,
|
|
18900
|
+
now
|
|
18901
|
+
);
|
|
18517
18902
|
continue;
|
|
18518
18903
|
}
|
|
18519
18904
|
for (const key of item.keys) az.tracker.commit(key, now);
|
|
@@ -18535,8 +18920,13 @@ async function tickOnce(deps) {
|
|
|
18535
18920
|
try {
|
|
18536
18921
|
await az.resolve(batch);
|
|
18537
18922
|
az.tracker.forgetResolved(batch);
|
|
18923
|
+
tickNoise.forget("anomaly-resolve");
|
|
18538
18924
|
} catch (e) {
|
|
18539
|
-
|
|
18925
|
+
warnRepeating(
|
|
18926
|
+
"anomaly-resolve",
|
|
18927
|
+
`[overmind] \u043D\u0430\u0434\u0437\u043E\u0440: \u0433\u0430\u0448\u0435\u043D\u0438\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0432\u0448\u0438\u0445\u0441\u044F \u0430\u043D\u043E\u043C\u0430\u043B\u0438\u0439 \u043D\u0435 \u0434\u043E\u043B\u0435\u0442\u0435\u043B\u043E, \u043F\u043E\u0432\u0442\u043E\u0440\u044E \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0442\u0438\u043A\u043E\u043C: ${describeError(e)}`,
|
|
18928
|
+
now
|
|
18929
|
+
);
|
|
18540
18930
|
}
|
|
18541
18931
|
}
|
|
18542
18932
|
}
|
|
@@ -19009,6 +19399,8 @@ async function reportWorkspaceAccess(tl, db) {
|
|
|
19009
19399
|
}
|
|
19010
19400
|
async function runDaemon(opts) {
|
|
19011
19401
|
installCrashGuards();
|
|
19402
|
+
installLogStamps();
|
|
19403
|
+
console.log(localZoneLine(/* @__PURE__ */ new Date()));
|
|
19012
19404
|
const daemonStart = captureDaemonStart();
|
|
19013
19405
|
const latestRelease = fetchLatestVersion();
|
|
19014
19406
|
const worktreesDir = await migrateWorktreesAtStartup();
|
|
@@ -19466,7 +19858,18 @@ async function runDaemon(opts) {
|
|
|
19466
19858
|
// TASWM-472: третья ось — ОЧЕРЕДЬ. Вливаем по одному и только на свежей базе; ридер
|
|
19467
19859
|
// свежести ходит в gh (`compare/base...head` → behind_by). Ключа нет при
|
|
19468
19860
|
// SWARM_MERGE_QUEUE=0 — тогда гейт отвечает ровно как до TASWM-472.
|
|
19469
|
-
|
|
19861
|
+
// TASWM-483: отказ по правам (403) очереди мержа приходит как «спросить не удалось» (null),
|
|
19862
|
+
// а не как отдельная ось: для НЕЁ это одно и то же — цвет проверок неизвестен, значит
|
|
19863
|
+
// вливать нельзя (checks_unknown, см. докблок computeMergeQueue про несимметричную цену).
|
|
19864
|
+
// Отличать 403 от таймаута нужно тому, кто ЖДЁТ и повторяет (handleCi), а очередь не ждёт.
|
|
19865
|
+
...SWARM_MERGE_QUEUE ? {
|
|
19866
|
+
baseSync: (url) => prBaseSync(url),
|
|
19867
|
+
checks: async (url) => {
|
|
19868
|
+
const ci = await prCiStatus(url);
|
|
19869
|
+
if (ci == null || ci.bucket === "denied") return null;
|
|
19870
|
+
return { bucket: ci.bucket };
|
|
19871
|
+
}
|
|
19872
|
+
} : {}
|
|
19470
19873
|
});
|
|
19471
19874
|
const deps = {
|
|
19472
19875
|
db,
|
|
@@ -19478,7 +19881,9 @@ async function runDaemon(opts) {
|
|
|
19478
19881
|
const repoOfRun = normalizeRepo(run.repo || opts.repo);
|
|
19479
19882
|
const taskClient = tlFor(run.workspace_id);
|
|
19480
19883
|
const [rawTask, verifyMode, projectBase, zoneNotes] = await Promise.all([
|
|
19481
|
-
|
|
19884
|
+
// TASWM-484: с причиной — на этом чтении стоит fail-loud ниже, и именно его
|
|
19885
|
+
// сообщение читает человек, когда дрон не поднялся.
|
|
19886
|
+
taskClient ? taskClient.getTaskDetailed(run.task_slug) : Promise.resolve(null),
|
|
19482
19887
|
verifyModeReader ? verifyModeReader(repoOfRun) : Promise.resolve(void 0),
|
|
19483
19888
|
baseBranchReader ? baseBranchReader(repoOfRun) : Promise.resolve(""),
|
|
19484
19889
|
// TASWM-466: память зоны прогона — тем же параллельным чтением, что и остальные
|
|
@@ -19576,6 +19981,11 @@ async function runDaemon(opts) {
|
|
|
19576
19981
|
// Классификация выхода прогона (TASWM-229): result-файл того же RUN_LOGS_DIR — «вышел сам»
|
|
19577
19982
|
// (graceful) vs «убит на полпути» (crash). Тот же каталог, что читает accountCost.
|
|
19578
19983
|
exitOutcome: makeExitClassifier(RUN_LOGS_DIR, (runId) => db.runLogKeys(runId), EXECUTOR),
|
|
19984
|
+
// TASWM-487: вид обрыва — из того же result-файла и теми же ключами каталога.
|
|
19985
|
+
exitAbort: makeAbortReader(RUN_LOGS_DIR, (runId) => db.runLogKeys(runId), EXECUTOR),
|
|
19986
|
+
// TASWM-487: жива ли работа на origin. Тот же фолбэк repo, что у removeWorktree выше:
|
|
19987
|
+
// у прогона в основном репозитории демона run.repo пуст.
|
|
19988
|
+
branchOnOrigin: (repo, branch) => remoteBranchExists(repo || opts.repo, branch),
|
|
19579
19989
|
// Живой child-процесс (TASWM-89): долгая Bash-команда (тесты/сборка) — не «завис».
|
|
19580
19990
|
livePpids,
|
|
19581
19991
|
// Погасить дрона (SIGTERM) — reap внешне-остановленных прогонов (kill из дашборда:
|
|
@@ -19735,7 +20145,7 @@ async function runDaemon(opts) {
|
|
|
19735
20145
|
// fresh-оживление строит промпт с нуля, и без тела задачи это тот же слепой дрон.
|
|
19736
20146
|
getTask: async (slug, ws) => {
|
|
19737
20147
|
const client = tlFor(ws);
|
|
19738
|
-
return client ? requireTaskBody(await client.
|
|
20148
|
+
return client ? requireTaskBody(await client.getTaskDetailed(slug), slug, ws) : null;
|
|
19739
20149
|
},
|
|
19740
20150
|
...verifyModeReader ? { verifyMode: verifyModeReader } : {},
|
|
19741
20151
|
// TASWM-466: тот же ридер памяти зоны, что на спавне — оживлённый дрон строит
|
|
@@ -20177,6 +20587,9 @@ if (isEntrypoint) {
|
|
|
20177
20587
|
}
|
|
20178
20588
|
}
|
|
20179
20589
|
export {
|
|
20590
|
+
ABORTED_CONTINUE_NUDGE,
|
|
20591
|
+
ABORTED_STOP_REASON,
|
|
20592
|
+
ABORT_TEXT,
|
|
20180
20593
|
AWAITING_CI_REASON,
|
|
20181
20594
|
BUDGET_STOP_REASON,
|
|
20182
20595
|
CHANGES_FRESH_WAKE_AFTER_MS,
|
|
@@ -20186,6 +20599,9 @@ export {
|
|
|
20186
20599
|
CHANGES_WAKE_LIMIT,
|
|
20187
20600
|
CHANGES_WAKE_MARK,
|
|
20188
20601
|
CI_CHECK_COOLDOWN_MS,
|
|
20602
|
+
CI_DENIED_LIMIT,
|
|
20603
|
+
CI_DENIED_MARK,
|
|
20604
|
+
CI_DENIED_STOP_MARK,
|
|
20189
20605
|
CI_NO_CHECKS_MARK,
|
|
20190
20606
|
CI_POLL_LIMIT,
|
|
20191
20607
|
CI_POLL_LIMIT_MARK,
|
|
@@ -20198,8 +20614,12 @@ export {
|
|
|
20198
20614
|
CI_WAKE_LIMIT,
|
|
20199
20615
|
CONFLICT_RESOLVE_COOLDOWN_MS,
|
|
20200
20616
|
CONTINUE_NUDGE,
|
|
20617
|
+
DEAD_HOLDER_SILENCE_MS,
|
|
20201
20618
|
EXITED_CONTINUE_NUDGE,
|
|
20202
20619
|
EXITED_NO_DONE_REASON,
|
|
20620
|
+
EXIT_WORK_LOST,
|
|
20621
|
+
EXIT_WORK_PUSHED,
|
|
20622
|
+
EXIT_WORK_UNKNOWN,
|
|
20203
20623
|
GATE_LOG_HEARTBEAT_TICKS,
|
|
20204
20624
|
HEADLESS_BLOCK,
|
|
20205
20625
|
HERO_CONTINUE_NUDGE,
|
|
@@ -20222,6 +20642,7 @@ export {
|
|
|
20222
20642
|
STORM_DEAD_LIMIT,
|
|
20223
20643
|
VERIFY_SPAWN_LIMIT,
|
|
20224
20644
|
WATCHDOG_PARK_REASON,
|
|
20645
|
+
abortReason,
|
|
20225
20646
|
buildChangesWakeNudge,
|
|
20226
20647
|
buildCiReadyNudge,
|
|
20227
20648
|
buildCiWakeNudge,
|