@taskless-app/swarm 0.6.21 → 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 +23 -3
- package/dist/overmind.js +268 -54
- package/package.json +2 -2
- package/roles/drone/skills/swarm-verify-green/SKILL.md +33 -0
package/dist/cli.js
CHANGED
|
@@ -1776,10 +1776,24 @@ function asTokenUsage(m) {
|
|
|
1776
1776
|
function budgetExhausted(result) {
|
|
1777
1777
|
return result.subtype === "error_max_budget_usd" || result.terminal_reason === "budget_exhausted";
|
|
1778
1778
|
}
|
|
1779
|
+
function sessionAbort(result) {
|
|
1780
|
+
if (budgetExhausted(result)) return null;
|
|
1781
|
+
const reason = result.terminal_reason ?? "";
|
|
1782
|
+
const subtype = result.subtype ?? "";
|
|
1783
|
+
if (reason === "aborted_tools") return { kind: "tools", raw: reason };
|
|
1784
|
+
if (reason === "aborted_streaming" || reason === "api_error") return { kind: "link", raw: reason };
|
|
1785
|
+
if (HONEST_ENDINGS.has(subtype) || HONEST_ENDINGS.has(reason)) return null;
|
|
1786
|
+
const failed = result.is_error === true || subtype === "error_during_execution" || reason.startsWith("aborted");
|
|
1787
|
+
if (!failed) return null;
|
|
1788
|
+
const raw = reason && reason !== "completed" ? reason : subtype || reason || "\u043E\u0448\u0438\u0431\u043A\u0430 \u0431\u0435\u0437 \u043C\u0435\u0442\u043A\u0438";
|
|
1789
|
+
return { kind: "other", raw };
|
|
1790
|
+
}
|
|
1791
|
+
var HONEST_ENDINGS;
|
|
1779
1792
|
var init_claudeResult = __esm({
|
|
1780
1793
|
"src/claudeResult.ts"() {
|
|
1781
1794
|
"use strict";
|
|
1782
1795
|
init_pricing();
|
|
1796
|
+
HONEST_ENDINGS = /* @__PURE__ */ new Set(["error_max_turns"]);
|
|
1783
1797
|
}
|
|
1784
1798
|
});
|
|
1785
1799
|
|
|
@@ -1854,6 +1868,9 @@ function writeMcpConfig(spec) {
|
|
|
1854
1868
|
writeFileSync(path, JSON.stringify(cfg, null, 2));
|
|
1855
1869
|
return path;
|
|
1856
1870
|
}
|
|
1871
|
+
function abortField(abort) {
|
|
1872
|
+
return abort ? { aborted: abort } : {};
|
|
1873
|
+
}
|
|
1857
1874
|
var ISOLATION_FLAGS, MCP_CONFIG_FILE, claudeCodeExecutor;
|
|
1858
1875
|
var init_claudeCode = __esm({
|
|
1859
1876
|
"src/executors/claudeCode.ts"() {
|
|
@@ -1892,7 +1909,10 @@ var init_claudeCode = __esm({
|
|
|
1892
1909
|
durationMs: spend.durationMs,
|
|
1893
1910
|
// TASWM-377: «упёрся в потолок» — отдельный факт об этом же итоге, а не отдельный
|
|
1894
1911
|
// разбор файла: у claude он лежит в тех же полях, что деньги и токены.
|
|
1895
|
-
...budgetExhausted(result) ? { stoppedByBudget: true } : {}
|
|
1912
|
+
...budgetExhausted(result) ? { stoppedByBudget: true } : {},
|
|
1913
|
+
// TASWM-487: и «оборвалось» — третий факт об этом же итоге. Порядок важен: потолок
|
|
1914
|
+
// трат sessionAbort отсекает сам, поэтому оба ключа одновременно не появятся.
|
|
1915
|
+
...abortField(sessionAbort(result))
|
|
1896
1916
|
};
|
|
1897
1917
|
},
|
|
1898
1918
|
session: {
|
|
@@ -2373,7 +2393,7 @@ var init_package = __esm({
|
|
|
2373
2393
|
"package.json"() {
|
|
2374
2394
|
package_default = {
|
|
2375
2395
|
name: "@taskless-app/swarm",
|
|
2376
|
-
version: "0.6.
|
|
2396
|
+
version: "0.6.22",
|
|
2377
2397
|
type: "module",
|
|
2378
2398
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
2379
2399
|
keywords: [
|
|
@@ -2423,7 +2443,7 @@ var init_package = __esm({
|
|
|
2423
2443
|
execa: "^10.0.0",
|
|
2424
2444
|
zod: "3.25.76"
|
|
2425
2445
|
},
|
|
2426
|
-
gitHead: "
|
|
2446
|
+
gitHead: "b8eaac4a5562ffcf45a02af338267620e0a7843f"
|
|
2427
2447
|
};
|
|
2428
2448
|
}
|
|
2429
2449
|
});
|
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
|
}
|
|
@@ -8881,6 +8901,80 @@ function rotateDaemonLog(path, opts, io = realIo) {
|
|
|
8881
8901
|
return "rotated";
|
|
8882
8902
|
}
|
|
8883
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
|
+
|
|
8884
8978
|
// src/repoRegistry.ts
|
|
8885
8979
|
init_config();
|
|
8886
8980
|
import { $ } from "execa";
|
|
@@ -8919,13 +9013,13 @@ async function buildRepoRegistry(paths) {
|
|
|
8919
9013
|
}
|
|
8920
9014
|
|
|
8921
9015
|
// src/repoMemory.ts
|
|
8922
|
-
import { readFileSync as
|
|
8923
|
-
import { join as
|
|
9016
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
9017
|
+
import { join as join9 } from "path";
|
|
8924
9018
|
var FILE = "repos.json";
|
|
8925
9019
|
function loadRememberedRepos(home) {
|
|
8926
9020
|
let raw;
|
|
8927
9021
|
try {
|
|
8928
|
-
raw =
|
|
9022
|
+
raw = readFileSync7(join9(home, FILE), "utf8");
|
|
8929
9023
|
} catch {
|
|
8930
9024
|
return [];
|
|
8931
9025
|
}
|
|
@@ -8940,7 +9034,7 @@ function loadRememberedRepos(home) {
|
|
|
8940
9034
|
function rememberRepos(home, paths, now = () => /* @__PURE__ */ new Date()) {
|
|
8941
9035
|
const clean = paths.filter((p) => p.trim().length > 0);
|
|
8942
9036
|
if (clean.length === 0) return;
|
|
8943
|
-
|
|
9037
|
+
writeFileSync7(join9(home, FILE), `${JSON.stringify({ paths: clean, at: now().toISOString() }, null, 2)}
|
|
8944
9038
|
`);
|
|
8945
9039
|
}
|
|
8946
9040
|
function pickRepoPaths(args) {
|
|
@@ -9185,9 +9279,13 @@ async function refreshRepoMap(opts) {
|
|
|
9185
9279
|
added++;
|
|
9186
9280
|
}
|
|
9187
9281
|
scannedAt = stamp();
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
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
|
+
}
|
|
9191
9289
|
}
|
|
9192
9290
|
const registry = [...clones.values()].map((c) => ({
|
|
9193
9291
|
repoFullName: c.repoFullName,
|
|
@@ -9199,8 +9297,10 @@ async function refreshRepoMap(opts) {
|
|
|
9199
9297
|
scannedAt
|
|
9200
9298
|
);
|
|
9201
9299
|
if (!answer.ok) {
|
|
9202
|
-
|
|
9203
|
-
|
|
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
|
+
]);
|
|
9204
9304
|
return registry;
|
|
9205
9305
|
}
|
|
9206
9306
|
opts.state.unknown = false;
|
|
@@ -9209,18 +9309,39 @@ async function refreshRepoMap(opts) {
|
|
|
9209
9309
|
opts.state.roots = [...opts.roots];
|
|
9210
9310
|
opts.state.at = stamp();
|
|
9211
9311
|
if (wantedNames.length === 0) {
|
|
9212
|
-
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"]);
|
|
9213
9313
|
return registry;
|
|
9214
9314
|
}
|
|
9215
|
-
log(`[overmind] ${describeMapSummary(opts.state, opts.roots)}`);
|
|
9216
|
-
for (const c of opts.state.clones) log(`[overmind] ${describeClone(c)}`);
|
|
9217
9315
|
const prefixOf = new Map(answer.repos.map((r) => [r.repoFullName, r.taskPrefix]));
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
log
|
|
9221
|
-
|
|
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
|
+
);
|
|
9222
9329
|
return registry;
|
|
9223
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
|
+
}
|
|
9224
9345
|
function describeMapSummary(state, roots) {
|
|
9225
9346
|
const found = state.clones.length;
|
|
9226
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)";
|
|
@@ -9917,17 +10038,20 @@ import { readdirSync as readdirSync10, readFileSync as readFileSync12 } from "fs
|
|
|
9917
10038
|
import { join as join16 } from "path";
|
|
9918
10039
|
init_executors();
|
|
9919
10040
|
function endedItself(outcome) {
|
|
9920
|
-
return outcome === "graceful" || outcome === "budget";
|
|
10041
|
+
return outcome === "graceful" || outcome === "budget" || outcome === "aborted";
|
|
10042
|
+
}
|
|
10043
|
+
function wasAborted(outcome) {
|
|
10044
|
+
return outcome === "aborted";
|
|
9921
10045
|
}
|
|
9922
10046
|
var RESULT_FILE_RE = /^(\d+)\.json(\.done)?$/;
|
|
9923
|
-
function
|
|
10047
|
+
function readLatestResultRaw(runLogsDir, runLogKeys) {
|
|
9924
10048
|
const dir = findRunLogDir(runLogsDir, runLogKeys);
|
|
9925
|
-
if (!dir) return
|
|
10049
|
+
if (!dir) return null;
|
|
9926
10050
|
let files;
|
|
9927
10051
|
try {
|
|
9928
10052
|
files = readdirSync10(dir);
|
|
9929
10053
|
} catch {
|
|
9930
|
-
return
|
|
10054
|
+
return null;
|
|
9931
10055
|
}
|
|
9932
10056
|
let latest = null;
|
|
9933
10057
|
for (const f of files) {
|
|
@@ -9936,21 +10060,35 @@ function classifyExit(runLogsDir, runLogKeys, executor = resolveExecutor()) {
|
|
|
9936
10060
|
const idx = Number(m[1]);
|
|
9937
10061
|
if (!latest || idx > latest.idx) latest = { idx, name: f };
|
|
9938
10062
|
}
|
|
9939
|
-
if (!latest) return
|
|
9940
|
-
let raw;
|
|
10063
|
+
if (!latest) return null;
|
|
9941
10064
|
try {
|
|
9942
|
-
|
|
10065
|
+
return readFileSync12(join16(dir, latest.name), "utf8");
|
|
9943
10066
|
} catch {
|
|
9944
|
-
return
|
|
10067
|
+
return null;
|
|
9945
10068
|
}
|
|
10069
|
+
}
|
|
10070
|
+
function classifyExit(runLogsDir, runLogKeys, executor = resolveExecutor()) {
|
|
10071
|
+
const raw = readLatestResultRaw(runLogsDir, runLogKeys);
|
|
10072
|
+
if (raw == null) return "unknown";
|
|
9946
10073
|
const result = executor.parseResult(raw, null);
|
|
9947
10074
|
if (!result) return "crash";
|
|
9948
|
-
|
|
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;
|
|
9949
10083
|
}
|
|
9950
10084
|
function makeExitClassifier(runLogsDir, runLogKeys, executor) {
|
|
9951
10085
|
const exec = executor ?? resolveExecutor();
|
|
9952
10086
|
return (run) => classifyExit(runLogsDir, runLogKeys(run.id), exec);
|
|
9953
10087
|
}
|
|
10088
|
+
function makeAbortReader(runLogsDir, runLogKeys, executor) {
|
|
10089
|
+
const exec = executor ?? resolveExecutor();
|
|
10090
|
+
return (run) => abortOfExit(runLogsDir, runLogKeys(run.id), exec);
|
|
10091
|
+
}
|
|
9954
10092
|
|
|
9955
10093
|
// src/orchestrator.ts
|
|
9956
10094
|
init_src();
|
|
@@ -13439,7 +13577,7 @@ async function changedPaths(worktree, opts = {}) {
|
|
|
13439
13577
|
// package.json
|
|
13440
13578
|
var package_default = {
|
|
13441
13579
|
name: "@taskless-app/swarm",
|
|
13442
|
-
version: "0.6.
|
|
13580
|
+
version: "0.6.22",
|
|
13443
13581
|
type: "module",
|
|
13444
13582
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
13445
13583
|
keywords: [
|
|
@@ -13489,7 +13627,7 @@ var package_default = {
|
|
|
13489
13627
|
execa: "^10.0.0",
|
|
13490
13628
|
zod: "3.25.76"
|
|
13491
13629
|
},
|
|
13492
|
-
gitHead: "
|
|
13630
|
+
gitHead: "b8eaac4a5562ffcf45a02af338267620e0a7843f"
|
|
13493
13631
|
};
|
|
13494
13632
|
|
|
13495
13633
|
// src/version.ts
|
|
@@ -15345,9 +15483,9 @@ async function runSelfUpdate(io) {
|
|
|
15345
15483
|
if (!exists(join28(repo, "pnpm-lock.yaml"))) {
|
|
15346
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" };
|
|
15347
15485
|
}
|
|
15348
|
-
const
|
|
15349
|
-
if (
|
|
15350
|
-
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)}` };
|
|
15351
15489
|
}
|
|
15352
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");
|
|
15353
15491
|
const built = await run("pnpm", ["--filter", `${DAEMON_PACKAGE}...`, "build"], repo);
|
|
@@ -15970,9 +16108,24 @@ var executorHasSession = (p) => EXECUTOR.session.exists(p);
|
|
|
15970
16108
|
var OVERMIND_ENTRY = THIS_FILE;
|
|
15971
16109
|
var STALL_MS = Number(process.env.SWARM_STALL_MS) || 10 * 60 * 1e3;
|
|
15972
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;
|
|
15973
16112
|
var RESPAWN_LIMIT = Number(process.env.SWARM_RESPAWN_LIMIT) || 2;
|
|
15974
16113
|
var RESPAWN_YOUNG_MS = Number(process.env.SWARM_RESPAWN_YOUNG_MS) || 60 * 1e3;
|
|
15975
|
-
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).";
|
|
15976
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.";
|
|
15977
16130
|
var STORM_DEAD_LIMIT = Number(process.env.SWARM_STORM_DEAD_LIMIT) || 3;
|
|
15978
16131
|
var CONFLICT_RESOLVE_COOLDOWN_MS = Number(process.env.SWARM_CONFLICT_COOLDOWN_MS) || 3 * 60 * 1e3;
|
|
@@ -16013,7 +16166,7 @@ var CHANGES_WAKE_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430
|
|
|
16013
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";
|
|
16014
16167
|
var CHANGES_WAKE_FAILED_MARK = "\u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C";
|
|
16015
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";
|
|
16016
|
-
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];
|
|
16017
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";
|
|
16018
16171
|
function reviewBudgetMark(cycle) {
|
|
16019
16172
|
return `${REVIEW_BUDGET_MARK} (\u043A\u0440\u0443\u0433 ${cycle})`;
|
|
@@ -16806,6 +16959,11 @@ function droneLabel(run) {
|
|
|
16806
16959
|
function signalTitle(run, what) {
|
|
16807
16960
|
return `${run.task_slug || droneLabel(run)}: ${what}`;
|
|
16808
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
|
+
}
|
|
16809
16967
|
function narrate(deps, text) {
|
|
16810
16968
|
try {
|
|
16811
16969
|
deps.narrate?.(text);
|
|
@@ -17577,14 +17735,17 @@ async function trySpawnQueuedRun(deps, run) {
|
|
|
17577
17735
|
return await failBeforeStart(deps, run, String(e));
|
|
17578
17736
|
}
|
|
17579
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
|
+
}
|
|
17580
17742
|
async function healBusyWorktree(deps, run, e) {
|
|
17581
17743
|
const path = e.worktreePath;
|
|
17582
17744
|
if (!deps.releaseWorktree) return false;
|
|
17583
17745
|
if (deps.liveWorktrees?.().has(path)) return false;
|
|
17746
|
+
const now = deps.now ? deps.now() : Date.now();
|
|
17584
17747
|
const holder = deps.db.listRuns().find((r) => r.id !== run.id && r.worktree_path === path && !isTerminalStatus(r.status));
|
|
17585
|
-
if (holder && (holder
|
|
17586
|
-
return false;
|
|
17587
|
-
}
|
|
17748
|
+
if (holder && holderStillHolds(deps, holder, now)) return false;
|
|
17588
17749
|
if (deps.isDirty && await deps.isDirty(path).catch(() => true)) {
|
|
17589
17750
|
deps.db.addEvent(
|
|
17590
17751
|
run.id,
|
|
@@ -17594,10 +17755,12 @@ async function healBusyWorktree(deps, run, e) {
|
|
|
17594
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.`);
|
|
17595
17756
|
return false;
|
|
17596
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";
|
|
17597
17760
|
await deps.releaseWorktree(run.repo, path).catch(() => {
|
|
17598
17761
|
});
|
|
17599
|
-
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)`);
|
|
17600
|
-
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}`);
|
|
17601
17764
|
return true;
|
|
17602
17765
|
}
|
|
17603
17766
|
async function failBeforeStart(deps, run, reason) {
|
|
@@ -17908,6 +18071,15 @@ async function buildReliefCostCandidatesFor(deps, now, droneCap) {
|
|
|
17908
18071
|
...hrefOf(worstRun?.task_url)
|
|
17909
18072
|
}));
|
|
17910
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
|
+
}
|
|
17911
18083
|
async function deliveredPrUrl(deps, run) {
|
|
17912
18084
|
if (run.pr_url) return run.pr_url;
|
|
17913
18085
|
if (!deps.openPrsForBranch) return null;
|
|
@@ -18299,7 +18471,11 @@ async function tickOnce(deps) {
|
|
|
18299
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`);
|
|
18300
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.`);
|
|
18301
18473
|
} catch (e) {
|
|
18302
|
-
|
|
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
|
+
);
|
|
18303
18479
|
}
|
|
18304
18480
|
}
|
|
18305
18481
|
continue;
|
|
@@ -18505,7 +18681,10 @@ async function tickOnce(deps) {
|
|
|
18505
18681
|
continue;
|
|
18506
18682
|
}
|
|
18507
18683
|
}
|
|
18508
|
-
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;
|
|
18509
18688
|
const wt = run.worktree_path;
|
|
18510
18689
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(wt);
|
|
18511
18690
|
if (deps.respawn && canContinue && run.respawns < RESPAWN_LIMIT) {
|
|
@@ -18513,21 +18692,31 @@ async function tickOnce(deps) {
|
|
|
18513
18692
|
deps.db.addEvent(
|
|
18514
18693
|
run.id,
|
|
18515
18694
|
"respawned",
|
|
18516
|
-
`\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.`
|
|
18517
18700
|
);
|
|
18518
|
-
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.`);
|
|
18519
18701
|
try {
|
|
18520
|
-
await deps.respawn(run, "continue", EXITED_CONTINUE_NUDGE);
|
|
18702
|
+
await deps.respawn(run, "continue", aborted ? ABORTED_CONTINUE_NUDGE : EXITED_CONTINUE_NUDGE);
|
|
18521
18703
|
} catch (e) {
|
|
18522
|
-
|
|
18523
|
-
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);
|
|
18524
18707
|
}
|
|
18525
18708
|
continue;
|
|
18526
18709
|
}
|
|
18527
|
-
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("; ");
|
|
18528
18714
|
deps.db.updateRun(run.id, { status: "paused", stop_reason: reason2 });
|
|
18529
18715
|
deps.db.addEvent(run.id, "paused", reason2);
|
|
18530
|
-
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
|
+
);
|
|
18531
18720
|
continue;
|
|
18532
18721
|
}
|
|
18533
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) {
|
|
@@ -18703,8 +18892,13 @@ async function tickOnce(deps) {
|
|
|
18703
18892
|
const signal = finalCall ? { ...item.signal, body: `${item.signal.body} ${LAST_REMINDER_SUFFIX}` } : item.signal;
|
|
18704
18893
|
try {
|
|
18705
18894
|
await az.notify(signal);
|
|
18895
|
+
tickNoise.forget(`notify:${signal.key}`);
|
|
18706
18896
|
} catch (e) {
|
|
18707
|
-
|
|
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
|
+
);
|
|
18708
18902
|
continue;
|
|
18709
18903
|
}
|
|
18710
18904
|
for (const key of item.keys) az.tracker.commit(key, now);
|
|
@@ -18726,8 +18920,13 @@ async function tickOnce(deps) {
|
|
|
18726
18920
|
try {
|
|
18727
18921
|
await az.resolve(batch);
|
|
18728
18922
|
az.tracker.forgetResolved(batch);
|
|
18923
|
+
tickNoise.forget("anomaly-resolve");
|
|
18729
18924
|
} catch (e) {
|
|
18730
|
-
|
|
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
|
+
);
|
|
18731
18930
|
}
|
|
18732
18931
|
}
|
|
18733
18932
|
}
|
|
@@ -19200,6 +19399,8 @@ async function reportWorkspaceAccess(tl, db) {
|
|
|
19200
19399
|
}
|
|
19201
19400
|
async function runDaemon(opts) {
|
|
19202
19401
|
installCrashGuards();
|
|
19402
|
+
installLogStamps();
|
|
19403
|
+
console.log(localZoneLine(/* @__PURE__ */ new Date()));
|
|
19203
19404
|
const daemonStart = captureDaemonStart();
|
|
19204
19405
|
const latestRelease = fetchLatestVersion();
|
|
19205
19406
|
const worktreesDir = await migrateWorktreesAtStartup();
|
|
@@ -19780,6 +19981,11 @@ async function runDaemon(opts) {
|
|
|
19780
19981
|
// Классификация выхода прогона (TASWM-229): result-файл того же RUN_LOGS_DIR — «вышел сам»
|
|
19781
19982
|
// (graceful) vs «убит на полпути» (crash). Тот же каталог, что читает accountCost.
|
|
19782
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),
|
|
19783
19989
|
// Живой child-процесс (TASWM-89): долгая Bash-команда (тесты/сборка) — не «завис».
|
|
19784
19990
|
livePpids,
|
|
19785
19991
|
// Погасить дрона (SIGTERM) — reap внешне-остановленных прогонов (kill из дашборда:
|
|
@@ -20381,6 +20587,9 @@ if (isEntrypoint) {
|
|
|
20381
20587
|
}
|
|
20382
20588
|
}
|
|
20383
20589
|
export {
|
|
20590
|
+
ABORTED_CONTINUE_NUDGE,
|
|
20591
|
+
ABORTED_STOP_REASON,
|
|
20592
|
+
ABORT_TEXT,
|
|
20384
20593
|
AWAITING_CI_REASON,
|
|
20385
20594
|
BUDGET_STOP_REASON,
|
|
20386
20595
|
CHANGES_FRESH_WAKE_AFTER_MS,
|
|
@@ -20405,8 +20614,12 @@ export {
|
|
|
20405
20614
|
CI_WAKE_LIMIT,
|
|
20406
20615
|
CONFLICT_RESOLVE_COOLDOWN_MS,
|
|
20407
20616
|
CONTINUE_NUDGE,
|
|
20617
|
+
DEAD_HOLDER_SILENCE_MS,
|
|
20408
20618
|
EXITED_CONTINUE_NUDGE,
|
|
20409
20619
|
EXITED_NO_DONE_REASON,
|
|
20620
|
+
EXIT_WORK_LOST,
|
|
20621
|
+
EXIT_WORK_PUSHED,
|
|
20622
|
+
EXIT_WORK_UNKNOWN,
|
|
20410
20623
|
GATE_LOG_HEARTBEAT_TICKS,
|
|
20411
20624
|
HEADLESS_BLOCK,
|
|
20412
20625
|
HERO_CONTINUE_NUDGE,
|
|
@@ -20429,6 +20642,7 @@ export {
|
|
|
20429
20642
|
STORM_DEAD_LIMIT,
|
|
20430
20643
|
VERIFY_SPAWN_LIMIT,
|
|
20431
20644
|
WATCHDOG_PARK_REASON,
|
|
20645
|
+
abortReason,
|
|
20432
20646
|
buildChangesWakeNudge,
|
|
20433
20647
|
buildCiReadyNudge,
|
|
20434
20648
|
buildCiWakeNudge,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taskless-app/swarm",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
6
6
|
"keywords": [
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"execa": "^10.0.0",
|
|
39
39
|
"zod": "3.25.76"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "b8eaac4a5562ffcf45a02af338267620e0a7843f",
|
|
42
42
|
"scripts": {
|
|
43
43
|
"overmind": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/overmind.ts",
|
|
44
44
|
"swarm": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/cli.ts",
|
|
@@ -46,6 +46,39 @@ CONTRIBUTING): КАКИМИ командами это зелёное получ
|
|
|
46
46
|
разрешение молча сдать: скажи прямо, чем проверял вместо них. Инфраструктуру
|
|
47
47
|
проекта под гейт роя НЕ достраивай.
|
|
48
48
|
|
|
49
|
+
## Тяжёлый прогон — в файл, а не в контекст
|
|
50
|
+
|
|
51
|
+
Вывод на тысячи строк не влезает в один результат инструмента, приходит пустым — и
|
|
52
|
+
сессия рвётся на полуслове, посреди твоей работы. В run-логах роя за июль–август это
|
|
53
|
+
**215 обрывов из 1104 выходов**: каждый пятый дрон умирает не от бага, а от
|
|
54
|
+
собственного `pnpm test`.
|
|
55
|
+
|
|
56
|
+
Порядок такой:
|
|
57
|
+
|
|
58
|
+
1. **Сначала спроси, надо ли гнать сьют целиком здесь.** Если рой работает в режиме
|
|
59
|
+
`verifyMode=ci`, общий прогон уже делает CI на этом же коммите — дублировать его на
|
|
60
|
+
машине владельца не надо (тебе про это скажут в задании). Твоё дело тогда — прицельно
|
|
61
|
+
те файлы, которых касается правка.
|
|
62
|
+
2. **Гонишь тяжёлое — пиши в файл, в контекст бери хвост.**
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
LOG=$(mktemp -t suite) # свой файл: на машине не ты один
|
|
66
|
+
pnpm test > "$LOG" 2>&1; echo "exit=$?" # код — от ТЕСТОВ, не от tail
|
|
67
|
+
tail -40 "$LOG" # итог: сколько файлов, сколько тестов
|
|
68
|
+
grep -nE "FAIL|✗|✘|●" "$LOG" | head -40 # упавшие — точечно
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- Код возврата бери у самой команды (`echo "exit=$?"` сразу после неё). `cmd | tail`
|
|
72
|
+
всегда вернёт 0 — это код `tail`, и красный сьют так маскируется.
|
|
73
|
+
- Свой файл на прогон (`mktemp`), не общий `/tmp/suite.log`: дронов на машине несколько,
|
|
74
|
+
и они затрут лог друг другу — а ты прочитаешь чужой результат как свой.
|
|
75
|
+
- Доказательство в сдачу — итоговые строки из файла (`Test Files … Tests …`, `exit=0`),
|
|
76
|
+
а не простыня целиком. Полнота доказательства — в цифрах итога, не в объёме.
|
|
77
|
+
- То же касается `git log`, `git diff` без пути, дампа большого файла, `docker build`:
|
|
78
|
+
всё, что печатает бесконечно, идёт через файл.
|
|
79
|
+
- Это не «частичный прогон» из п. 2: прогон полный, обрезан только вывод. Экстраполяция
|
|
80
|
+
запрещена по-прежнему — итог читай, а не первые строки.
|
|
81
|
+
|
|
49
82
|
## Red flags — stop and go get evidence
|
|
50
83
|
|
|
51
84
|
- «should», «probably», «seems to», «кажется, работает»
|