@taskless-app/swarm 0.6.24 → 0.6.26
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 +3986 -3314
- package/dist/overmind.js +1057 -618
- package/hooks/pre-merge-gate.sh +355 -0
- package/package.json +3 -2
package/dist/overmind.js
CHANGED
|
@@ -9,17 +9,23 @@ var __export = (target, all) => {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
// ../swarm-core/src/types.ts
|
|
12
|
+
function holdsFiles(run) {
|
|
13
|
+
if ((run.kind ?? "drone") !== "drone") return false;
|
|
14
|
+
if (run.status === "review") return !!run.pr_url;
|
|
15
|
+
return ACTIVE_DRONE_STATUSES.has(run.status);
|
|
16
|
+
}
|
|
12
17
|
function isMilestoneKind(kind) {
|
|
13
18
|
return MILESTONE_KIND_SET.has(kind);
|
|
14
19
|
}
|
|
15
20
|
function toRunKind(role) {
|
|
16
21
|
return role === "hero" || role === "reviewer" || role === "verifier" ? role : "drone";
|
|
17
22
|
}
|
|
18
|
-
var ACTIVE_DRONE_STATUSES, EVENT_KINDS, MILESTONE_KINDS, MILESTONE_KIND_SET;
|
|
23
|
+
var ACTIVE_DRONE_STATUSES, FILE_HOLDING_STATUSES, EVENT_KINDS, MILESTONE_KINDS, MILESTONE_KIND_SET;
|
|
19
24
|
var init_types = __esm({
|
|
20
25
|
"../swarm-core/src/types.ts"() {
|
|
21
26
|
"use strict";
|
|
22
27
|
ACTIVE_DRONE_STATUSES = /* @__PURE__ */ new Set(["running", "blocked"]);
|
|
28
|
+
FILE_HOLDING_STATUSES = /* @__PURE__ */ new Set([...ACTIVE_DRONE_STATUSES, "review"]);
|
|
23
29
|
EVENT_KINDS = [
|
|
24
30
|
"spawned",
|
|
25
31
|
"step",
|
|
@@ -525,6 +531,86 @@ var init_mergeQueue = __esm({
|
|
|
525
531
|
}
|
|
526
532
|
});
|
|
527
533
|
|
|
534
|
+
// ../swarm-core/src/prRef.ts
|
|
535
|
+
function prNumberOf(ref) {
|
|
536
|
+
const s = (ref || "").trim();
|
|
537
|
+
if (!s) return null;
|
|
538
|
+
const url = s.match(/\/pull\/(\d+)/);
|
|
539
|
+
if (url) return Number(url[1]);
|
|
540
|
+
const bare = s.match(/^#?(\d+)$/);
|
|
541
|
+
return bare ? Number(bare[1]) : null;
|
|
542
|
+
}
|
|
543
|
+
function repoKeyOf(raw) {
|
|
544
|
+
const s = (raw ?? "").trim().replace(/\/+$/, "").replace(/\.git$/i, "");
|
|
545
|
+
if (!s) return null;
|
|
546
|
+
const url = s.match(/github\.com[:/]([^/\s]+)\/([^/\s]+?)(?:\/(?:pull|tree|compare|commit|issues)\/.*)?$/i);
|
|
547
|
+
if (url) return `${url[1]}/${url[2]}`.toLowerCase();
|
|
548
|
+
const pair = s.match(/^([\w.-]+)\/([\w.-]+)$/);
|
|
549
|
+
return pair ? `${pair[1]}/${pair[2]}`.toLowerCase() : null;
|
|
550
|
+
}
|
|
551
|
+
function newest(rows) {
|
|
552
|
+
let best = null;
|
|
553
|
+
for (const r of rows) if (!best || r.order > best.order) best = r;
|
|
554
|
+
return best;
|
|
555
|
+
}
|
|
556
|
+
function pick(rows) {
|
|
557
|
+
return newest(rows.filter((r) => r.author)) ?? newest(rows);
|
|
558
|
+
}
|
|
559
|
+
function pickRunForPr(rows, ref, ctxRepo) {
|
|
560
|
+
const num = prNumberOf(ref);
|
|
561
|
+
if (num == null) return null;
|
|
562
|
+
const target = repoKeyOf(ref) ?? repoKeyOf(ctxRepo);
|
|
563
|
+
const hits = rows.filter((r) => r.prUrl && prNumberOf(r.prUrl) === num);
|
|
564
|
+
if (hits.length === 0) return null;
|
|
565
|
+
if (target) {
|
|
566
|
+
const mine = hits.filter((r) => keyOf(r) === target);
|
|
567
|
+
const blind = hits.filter((r) => keyOf(r) == null);
|
|
568
|
+
return pick(mine.length ? mine : blind);
|
|
569
|
+
}
|
|
570
|
+
const keys = new Set(hits.map((r) => keyOf(r) ?? ""));
|
|
571
|
+
if (keys.size === 1) return pick(hits);
|
|
572
|
+
const risky = hits.filter((r) => r.reviewVerdict !== "approve");
|
|
573
|
+
return pick(risky.length ? risky : hits);
|
|
574
|
+
}
|
|
575
|
+
function keyOf(r) {
|
|
576
|
+
return repoKeyOf(r.prUrl) ?? repoKeyOf(r.repoKey);
|
|
577
|
+
}
|
|
578
|
+
var init_prRef = __esm({
|
|
579
|
+
"../swarm-core/src/prRef.ts"() {
|
|
580
|
+
"use strict";
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
// ../swarm-core/src/returnReason.ts
|
|
585
|
+
function isReturnReason(v) {
|
|
586
|
+
return typeof v === "string" && RETURN_REASON_SET.has(v);
|
|
587
|
+
}
|
|
588
|
+
function toReturnReason(v) {
|
|
589
|
+
return isReturnReason(v) ? v : null;
|
|
590
|
+
}
|
|
591
|
+
function returnReasonLabel(r) {
|
|
592
|
+
switch (r) {
|
|
593
|
+
case "unclear_goal":
|
|
594
|
+
return "\u043D\u0435\u043F\u043E\u043D\u044F\u0442\u043D\u043E, \u0447\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C";
|
|
595
|
+
case "no_acceptance":
|
|
596
|
+
return "\u043D\u0435\u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442";
|
|
597
|
+
case "too_big":
|
|
598
|
+
return "\u0441\u043B\u0438\u0448\u043A\u043E\u043C \u0432\u0435\u043B\u0438\u043A\u0430 \u0434\u043B\u044F \u043E\u0434\u043D\u043E\u0433\u043E \u043F\u0440\u043E\u0433\u043E\u043D\u0430";
|
|
599
|
+
case "contradiction":
|
|
600
|
+
return "\u043F\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0442 \u0441\u0430\u043C\u0430 \u0441\u0435\u0431\u0435";
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
var RETURN_REASONS, RETURN_REASON_SET, UNRETURNED_FAILURE_STATUSES, UNRETURNED_FAILURE_SET;
|
|
604
|
+
var init_returnReason = __esm({
|
|
605
|
+
"../swarm-core/src/returnReason.ts"() {
|
|
606
|
+
"use strict";
|
|
607
|
+
RETURN_REASONS = ["unclear_goal", "no_acceptance", "too_big", "contradiction"];
|
|
608
|
+
RETURN_REASON_SET = new Set(RETURN_REASONS);
|
|
609
|
+
UNRETURNED_FAILURE_STATUSES = ["failed", "blocked"];
|
|
610
|
+
UNRETURNED_FAILURE_SET = new Set(UNRETURNED_FAILURE_STATUSES);
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
|
|
528
614
|
// ../swarm-core/src/modelPolicy.ts
|
|
529
615
|
function findProvider(id) {
|
|
530
616
|
const key = typeof id === "string" ? id.trim().toLowerCase() : "";
|
|
@@ -1632,7 +1718,9 @@ var init_src = __esm({
|
|
|
1632
1718
|
init_reviewPhase();
|
|
1633
1719
|
init_mergeGate();
|
|
1634
1720
|
init_mergeQueue();
|
|
1721
|
+
init_prRef();
|
|
1635
1722
|
init_verifyVerdict();
|
|
1723
|
+
init_returnReason();
|
|
1636
1724
|
init_modelPolicy();
|
|
1637
1725
|
init_status();
|
|
1638
1726
|
init_deps();
|
|
@@ -1784,6 +1872,43 @@ var init_swarmFiles = __esm({
|
|
|
1784
1872
|
}
|
|
1785
1873
|
});
|
|
1786
1874
|
|
|
1875
|
+
// src/mergeGateHook.ts
|
|
1876
|
+
import { chmodSync, copyFileSync, existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1877
|
+
import { homedir } from "os";
|
|
1878
|
+
import { dirname, join as join2 } from "path";
|
|
1879
|
+
import { fileURLToPath } from "url";
|
|
1880
|
+
function packagedHookPath() {
|
|
1881
|
+
return join2(dirname(fileURLToPath(import.meta.url)), "..", "hooks", HOOK_FILE);
|
|
1882
|
+
}
|
|
1883
|
+
function wiredCommands(settings) {
|
|
1884
|
+
const blocks = settings?.hooks?.PreToolUse;
|
|
1885
|
+
if (!Array.isArray(blocks)) return [];
|
|
1886
|
+
return blocks.flatMap((b) => Array.isArray(b?.hooks) ? b.hooks : []).flatMap((h) => isOurHook(h?.command) ? [h.command] : []);
|
|
1887
|
+
}
|
|
1888
|
+
function readSettings(path) {
|
|
1889
|
+
if (!existsSync3(path)) return void 0;
|
|
1890
|
+
try {
|
|
1891
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
1892
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
1893
|
+
} catch {
|
|
1894
|
+
return null;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
function droneMergeGateHookPath(worktree, packaged = packagedHookPath()) {
|
|
1898
|
+
if (!existsSync3(packaged)) return null;
|
|
1899
|
+
const projectSettings = readSettings(join2(worktree, ".claude", "settings.json"));
|
|
1900
|
+
if (projectSettings && wiredCommands(projectSettings).length > 0) return null;
|
|
1901
|
+
return packaged;
|
|
1902
|
+
}
|
|
1903
|
+
var HOOK_FILE, isOurHook;
|
|
1904
|
+
var init_mergeGateHook = __esm({
|
|
1905
|
+
"src/mergeGateHook.ts"() {
|
|
1906
|
+
"use strict";
|
|
1907
|
+
HOOK_FILE = "pre-merge-gate.sh";
|
|
1908
|
+
isOurHook = (command) => typeof command === "string" && command.trim().endsWith(HOOK_FILE);
|
|
1909
|
+
}
|
|
1910
|
+
});
|
|
1911
|
+
|
|
1787
1912
|
// src/pricing.ts
|
|
1788
1913
|
function costFromUsage(usage, model, table = MODEL_PRICING) {
|
|
1789
1914
|
const entry = findModelPricing(model, table);
|
|
@@ -1872,9 +1997,9 @@ var init_claudeResult = __esm({
|
|
|
1872
1997
|
});
|
|
1873
1998
|
|
|
1874
1999
|
// src/executors/claudeCode.ts
|
|
1875
|
-
import { readdirSync as readdirSync2, statSync, writeFileSync as
|
|
1876
|
-
import { homedir } from "os";
|
|
1877
|
-
import { join as
|
|
2000
|
+
import { readdirSync as readdirSync2, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
2001
|
+
import { homedir as homedir2 } from "os";
|
|
2002
|
+
import { join as join3 } from "path";
|
|
1878
2003
|
function claudeArgs(cfgPath, prompt, continueSession = false, model, pluginDirs = [], settings, maxBudgetUsd, effort) {
|
|
1879
2004
|
const pluginFlags = pluginDirs.flatMap((dir) => ["--plugin-dir", dir]);
|
|
1880
2005
|
const budgetFlags = typeof maxBudgetUsd === "number" && maxBudgetUsd > 0 ? ["--max-budget-usd", String(maxBudgetUsd)] : [];
|
|
@@ -1900,20 +2025,23 @@ function droneDenyRules(paths) {
|
|
|
1900
2025
|
const abs = (p) => `//${p.replace(/^\/+/, "")}`;
|
|
1901
2026
|
return paths.flatMap((p) => [`Read(${abs(p)})`, `Edit(${abs(p)})`]);
|
|
1902
2027
|
}
|
|
1903
|
-
function droneSettingsJson(paths) {
|
|
1904
|
-
|
|
2028
|
+
function droneSettingsJson(paths, mergeGateHook = null) {
|
|
2029
|
+
const settings = {};
|
|
2030
|
+
if (paths.length > 0) settings.permissions = { deny: droneDenyRules(paths) };
|
|
2031
|
+
if (mergeGateHook) settings.hooks = { PreToolUse: [{ matcher: "Bash", hooks: [{ type: "command", command: mergeGateHook }] }] };
|
|
2032
|
+
return JSON.stringify(settings);
|
|
1905
2033
|
}
|
|
1906
2034
|
function transcriptDir(worktree, projectsDir) {
|
|
1907
|
-
return
|
|
2035
|
+
return join3(projectsDir, worktree.replace(/[/.]/g, "-"));
|
|
1908
2036
|
}
|
|
1909
|
-
function sessionTranscriptExists(worktree, projectsDir =
|
|
2037
|
+
function sessionTranscriptExists(worktree, projectsDir = join3(homedir2(), ".claude", "projects")) {
|
|
1910
2038
|
try {
|
|
1911
2039
|
return readdirSync2(transcriptDir(worktree, projectsDir)).some((f) => f.endsWith(".jsonl"));
|
|
1912
2040
|
} catch {
|
|
1913
2041
|
return false;
|
|
1914
2042
|
}
|
|
1915
2043
|
}
|
|
1916
|
-
function transcriptMtimeMs(worktree, projectsDir =
|
|
2044
|
+
function transcriptMtimeMs(worktree, projectsDir = join3(homedir2(), ".claude", "projects")) {
|
|
1917
2045
|
const dir = transcriptDir(worktree, projectsDir);
|
|
1918
2046
|
let files;
|
|
1919
2047
|
try {
|
|
@@ -1925,7 +2053,7 @@ function transcriptMtimeMs(worktree, projectsDir = join2(homedir(), ".claude", "
|
|
|
1925
2053
|
for (const f of files) {
|
|
1926
2054
|
if (!f.endsWith(".jsonl")) continue;
|
|
1927
2055
|
try {
|
|
1928
|
-
const mtime2 = statSync(
|
|
2056
|
+
const mtime2 = statSync(join3(dir, f)).mtimeMs;
|
|
1929
2057
|
if (latest === null || mtime2 > latest) latest = mtime2;
|
|
1930
2058
|
} catch {
|
|
1931
2059
|
}
|
|
@@ -1938,8 +2066,8 @@ function writeMcpConfig(spec) {
|
|
|
1938
2066
|
[spec.mcp.name]: { command: spec.mcp.command, args: spec.mcp.args, env: spec.mcp.env }
|
|
1939
2067
|
}
|
|
1940
2068
|
};
|
|
1941
|
-
const path =
|
|
1942
|
-
|
|
2069
|
+
const path = join3(spec.worktree, MCP_CONFIG_FILE);
|
|
2070
|
+
writeFileSync3(path, JSON.stringify(cfg, null, 2));
|
|
1943
2071
|
return path;
|
|
1944
2072
|
}
|
|
1945
2073
|
function abortField(abort) {
|
|
@@ -1951,6 +2079,7 @@ var init_claudeCode = __esm({
|
|
|
1951
2079
|
"use strict";
|
|
1952
2080
|
init_claudeBin();
|
|
1953
2081
|
init_swarmFiles();
|
|
2082
|
+
init_mergeGateHook();
|
|
1954
2083
|
init_claudeResult();
|
|
1955
2084
|
ISOLATION_FLAGS = ["--setting-sources", "project", "--strict-mcp-config"];
|
|
1956
2085
|
MCP_CONFIG_FILE = SWARM_MCP_CONFIG_FILE;
|
|
@@ -1964,7 +2093,8 @@ var init_claudeCode = __esm({
|
|
|
1964
2093
|
// проброс колбэком), и `this` тогда уедет в undefined.
|
|
1965
2094
|
plan: (spec) => {
|
|
1966
2095
|
const cfgPath = writeMcpConfig(spec);
|
|
1967
|
-
const
|
|
2096
|
+
const hook = droneMergeGateHookPath(spec.worktree);
|
|
2097
|
+
const settings = spec.denyPaths.length > 0 || hook ? droneSettingsJson(spec.denyPaths, hook) : void 0;
|
|
1968
2098
|
return {
|
|
1969
2099
|
bin: resolveClaudeBin(),
|
|
1970
2100
|
args: claudeArgs(cfgPath, spec.prompt, spec.continueSession, spec.model, spec.skillDirs, settings, spec.maxBudgetUsd, spec.effort)
|
|
@@ -2006,35 +2136,35 @@ var init_claudeCode = __esm({
|
|
|
2006
2136
|
});
|
|
2007
2137
|
|
|
2008
2138
|
// src/swarmHome.ts
|
|
2009
|
-
import { homedir as
|
|
2010
|
-
import { join as
|
|
2011
|
-
import { mkdirSync } from "fs";
|
|
2139
|
+
import { homedir as homedir3 } from "os";
|
|
2140
|
+
import { join as join4 } from "path";
|
|
2141
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
2012
2142
|
var SWARM_HOME;
|
|
2013
2143
|
var init_swarmHome = __esm({
|
|
2014
2144
|
"src/swarmHome.ts"() {
|
|
2015
2145
|
"use strict";
|
|
2016
|
-
SWARM_HOME = process.env.SWARM_HOME ||
|
|
2017
|
-
|
|
2146
|
+
SWARM_HOME = process.env.SWARM_HOME || join4(homedir3(), ".taskless-swarm");
|
|
2147
|
+
mkdirSync2(SWARM_HOME, { recursive: true });
|
|
2018
2148
|
}
|
|
2019
2149
|
});
|
|
2020
2150
|
|
|
2021
2151
|
// src/executors/codex.ts
|
|
2022
|
-
import { existsSync as
|
|
2023
|
-
import { join as
|
|
2152
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, statSync as statSync2, symlinkSync } from "fs";
|
|
2153
|
+
import { join as join5 } from "path";
|
|
2024
2154
|
function resolveCodexBin(env = process.env) {
|
|
2025
2155
|
return (env.SWARM_CODEX_BIN ?? "").trim() || "codex";
|
|
2026
2156
|
}
|
|
2027
2157
|
function codexHomePath(worktree, swarmHome = SWARM_HOME) {
|
|
2028
|
-
return
|
|
2158
|
+
return join5(swarmHome, "executor-state", "codex", worktree.replace(/[/.]/g, "-"));
|
|
2029
2159
|
}
|
|
2030
2160
|
function ensureCodexHome(worktree, realHome, env = process.env, swarmHome = SWARM_HOME) {
|
|
2031
2161
|
const home = codexHomePath(worktree, swarmHome);
|
|
2032
|
-
|
|
2033
|
-
const real = (env.CODEX_HOME ?? "").trim() ||
|
|
2162
|
+
mkdirSync3(home, { recursive: true });
|
|
2163
|
+
const real = (env.CODEX_HOME ?? "").trim() || join5(realHome, ".codex");
|
|
2034
2164
|
for (const file of ["auth.json", "config.toml"]) {
|
|
2035
|
-
const target =
|
|
2036
|
-
const link =
|
|
2037
|
-
if (!
|
|
2165
|
+
const target = join5(real, file);
|
|
2166
|
+
const link = join5(home, file);
|
|
2167
|
+
if (!existsSync4(target) || existsSync4(link)) continue;
|
|
2038
2168
|
try {
|
|
2039
2169
|
symlinkSync(target, link);
|
|
2040
2170
|
} catch {
|
|
@@ -2098,7 +2228,7 @@ function newestRolloutMtime(dir, depth = 4) {
|
|
|
2098
2228
|
}
|
|
2099
2229
|
let latest = null;
|
|
2100
2230
|
for (const name of entries) {
|
|
2101
|
-
const full =
|
|
2231
|
+
const full = join5(dir, name);
|
|
2102
2232
|
let st;
|
|
2103
2233
|
try {
|
|
2104
2234
|
st = statSync2(full);
|
|
@@ -2172,8 +2302,8 @@ ${notice}` : spec.prompt;
|
|
|
2172
2302
|
return est.warning ? { costUsd: est.costUsd, warning: est.warning } : { costUsd: est.costUsd };
|
|
2173
2303
|
},
|
|
2174
2304
|
session: {
|
|
2175
|
-
exists: (worktree) => newestRolloutMtime(
|
|
2176
|
-
mtimeMs: (worktree) => newestRolloutMtime(
|
|
2305
|
+
exists: (worktree) => newestRolloutMtime(join5(codexHomePath(worktree), "sessions")) !== null,
|
|
2306
|
+
mtimeMs: (worktree) => newestRolloutMtime(join5(codexHomePath(worktree), "sessions"))
|
|
2177
2307
|
},
|
|
2178
2308
|
probe: {
|
|
2179
2309
|
// Тот же принцип, что у claude: дешёвый реальный прогон — он проверяет и auth, и живость
|
|
@@ -2298,6 +2428,7 @@ __export(config_exports, {
|
|
|
2298
2428
|
SWARM_WORKTREE_REAP_INTERVAL_MS: () => SWARM_WORKTREE_REAP_INTERVAL_MS,
|
|
2299
2429
|
TASKLESS_API: () => TASKLESS_API,
|
|
2300
2430
|
TASKLESS_MCP_PATH: () => TASKLESS_MCP_PATH,
|
|
2431
|
+
TASKLESS_REST_PATH: () => TASKLESS_REST_PATH,
|
|
2301
2432
|
TASKLESS_TASK_URL_BASE: () => TASKLESS_TASK_URL_BASE,
|
|
2302
2433
|
TASKLESS_TOKEN: () => TASKLESS_TOKEN,
|
|
2303
2434
|
TASKLESS_WORKSPACE: () => TASKLESS_WORKSPACE,
|
|
@@ -2309,14 +2440,16 @@ __export(config_exports, {
|
|
|
2309
2440
|
WORKTREES_NOINDEX_DIR: () => WORKTREES_NOINDEX_DIR,
|
|
2310
2441
|
droneWorktreePath: () => droneWorktreePath,
|
|
2311
2442
|
droneWorktreeRoot: () => droneWorktreeRoot,
|
|
2443
|
+
effectiveRepoOf: () => effectiveRepoOf,
|
|
2312
2444
|
mcpEndpoint: () => mcpEndpoint,
|
|
2313
2445
|
migrateWorktreesToNoindex: () => migrateWorktreesToNoindex,
|
|
2314
2446
|
normalizeRepo: () => normalizeRepo,
|
|
2315
2447
|
resolveEffectiveWorktreesDir: () => resolveEffectiveWorktreesDir,
|
|
2448
|
+
restEndpoint: () => restEndpoint,
|
|
2316
2449
|
reviewCutoffAt: () => reviewCutoffAt
|
|
2317
2450
|
});
|
|
2318
|
-
import { basename, join as
|
|
2319
|
-
import { readFileSync as
|
|
2451
|
+
import { basename, join as join6, resolve } from "path";
|
|
2452
|
+
import { readFileSync as readFileSync3, realpathSync, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
2320
2453
|
function normalizeRepo(repo) {
|
|
2321
2454
|
const r = (repo ?? "").trim();
|
|
2322
2455
|
if (!r) return "";
|
|
@@ -2326,11 +2459,14 @@ function normalizeRepo(repo) {
|
|
|
2326
2459
|
return resolve(r);
|
|
2327
2460
|
}
|
|
2328
2461
|
}
|
|
2462
|
+
function effectiveRepoOf(repo, fallback) {
|
|
2463
|
+
return normalizeRepo(repo || fallback || "");
|
|
2464
|
+
}
|
|
2329
2465
|
function droneWorktreePath(worktreesDir, repo, slug) {
|
|
2330
|
-
return
|
|
2466
|
+
return join6(droneWorktreeRoot(worktreesDir, repo), slug);
|
|
2331
2467
|
}
|
|
2332
2468
|
function droneWorktreeRoot(worktreesDir, repo) {
|
|
2333
|
-
return
|
|
2469
|
+
return join6(worktreesDir, basename(normalizeRepo(repo)));
|
|
2334
2470
|
}
|
|
2335
2471
|
function resolveEffectiveWorktreesDir(deps) {
|
|
2336
2472
|
if (deps.envOverride) return deps.envOverride;
|
|
@@ -2365,6 +2501,12 @@ function envNum(name, def) {
|
|
|
2365
2501
|
const n = Number(raw);
|
|
2366
2502
|
return Number.isFinite(n) && n >= 0 ? n : def;
|
|
2367
2503
|
}
|
|
2504
|
+
function restEndpoint(api, route, prefix = TASKLESS_REST_PATH) {
|
|
2505
|
+
const base3 = api.replace(/\/$/, "");
|
|
2506
|
+
const p = prefix === "/" ? "" : prefix.startsWith("/") ? prefix.replace(/\/$/, "") : `/${prefix.replace(/\/$/, "")}`;
|
|
2507
|
+
const r = route.startsWith("/") ? route : `/${route}`;
|
|
2508
|
+
return `${base3}${p}${r}`;
|
|
2509
|
+
}
|
|
2368
2510
|
function mcpEndpoint(api, path = TASKLESS_MCP_PATH) {
|
|
2369
2511
|
const base3 = api.replace(/\/$/, "");
|
|
2370
2512
|
const p = path.startsWith("/") ? path : `/${path}`;
|
|
@@ -2372,27 +2514,27 @@ function mcpEndpoint(api, path = TASKLESS_MCP_PATH) {
|
|
|
2372
2514
|
}
|
|
2373
2515
|
function tokenFromFile() {
|
|
2374
2516
|
try {
|
|
2375
|
-
return
|
|
2517
|
+
return readFileSync3(join6(SWARM_HOME, "token"), "utf8").trim();
|
|
2376
2518
|
} catch {
|
|
2377
2519
|
return "";
|
|
2378
2520
|
}
|
|
2379
2521
|
}
|
|
2380
2522
|
function reviewCutoffAt() {
|
|
2381
2523
|
try {
|
|
2382
|
-
const raw = JSON.parse(
|
|
2524
|
+
const raw = JSON.parse(readFileSync3(REVIEW_CUTOFF_PATH, "utf8"));
|
|
2383
2525
|
if (raw.at) return raw.at;
|
|
2384
2526
|
} catch {
|
|
2385
2527
|
}
|
|
2386
2528
|
const at = stamp();
|
|
2387
2529
|
try {
|
|
2388
2530
|
const tmp = `${REVIEW_CUTOFF_PATH}.tmp`;
|
|
2389
|
-
|
|
2531
|
+
writeFileSync4(tmp, JSON.stringify({ at }));
|
|
2390
2532
|
renameSync2(tmp, REVIEW_CUTOFF_PATH);
|
|
2391
2533
|
} catch {
|
|
2392
2534
|
}
|
|
2393
2535
|
return at;
|
|
2394
2536
|
}
|
|
2395
|
-
var DB_PATH, EXECUTOR, DRONE_HOME, DRONE_HOME_ISOLATION, DRONE_ISOLATION, WORKTREES_NOINDEX_DIR, WORKTREES_LEGACY_DIR, WORKTREES_DIR, RUN_LOGS_DIR, DAEMON_LOG_PATH, DAEMON_LOG_MAX_BYTES, DAEMON_LOG_KEEP, DAEMON_LOG_CHECK_MS, DIST_BUILD, SWARM_BACKEND, SWARM_OUTBOX, SWARM_MAX_IN_FLIGHT, SWARM_CLAIM_INTERVAL_MS, SWARM_MAX_LOAD_FACTOR, SWARM_MIN_FREE_GB, SWARM_MAX_FD_LOAD_FACTOR, SWARM_MAX_CPU_BUSY, SWARM_CPU_WINDOW_MS, SWARM_CPU_SANITY_BUSY, SWARM_CPU_SANITY_LOAD_FACTOR, SWARM_CPU_SANITY_LAG_MS, SWARM_RELIEF_LOAD_FACTOR, SWARM_RELIEF_MIN_FREE_GB, SWARM_RELIEF_MAX_FD_LOAD_FACTOR, SWARM_RELIEF_CPU_BUSY, SWARM_RELIEF_COST_WINDOW_MS, SWARM_RELIEF_ALERT_PAUSES, SWARM_RELIEF_ALERT_LOST_MS, SWARM_RELIEF_ALERT_MIN_STEPS, SWARM_RELIEF_ALERT_SUSTAIN_MS, SWARM_LOW_DISK_GB, SWARM_REHYDRATE_MS, SWARM_DRONE_BRIDGE_MS, SWARM_PROD_HEARTBEAT_MS, SWARM_VERSION_CHECK_MS, SWARM_SWEEP_INTERVAL_MS, SWARM_WORKTREE_REAP_INTERVAL_MS, DRONE_CHANNEL_ENABLED, DRONE_CHANNEL_PORT, DRONE_CHANNEL_URL, DRONE_CHANNEL_SECRET_PATH, DRONE_CHANNEL_GRACE_MS, HEARTBEAT_PATH, HEARTBEAT_STALE_MS, SWARM_REPO, SWARM_SCAN_DEPTH, SWARM_REPO_PATHS, SWARM_SCAN_ROOTS_RAW, REPO_MAP_PATH, SWARM_REPO_MAP_MS, SWARM_REPO_SCAN_TTL_MS, TASKLESS_API, SWARM_SHOT_TIMEOUT_MS, TASKLESS_MCP_PATH, TASKLESS_TOKEN, TASKLESS_WORKSPACE, TASKLESS_WORKSPACE_SLUG, TASKLESS_TASK_URL_BASE, SWARM_VERIFY, SWARM_AUTO_REVIEW, SWARM_AUTO_VERIFY, SWARM_MERGE_QUEUE, REVIEW_CUTOFF_PATH, SWARM_REVIEW_CAP, SWARM_STORM_URL, WEB_PORT, WEB_REDIRECT_URI, SWARM_ANOMALY_RENOTIFY_MS, SWARM_ANOMALY_MAX_REPEATS, SWARM_ANOMALY_RUN_DOWN_WINDOW_MS, SWARM_ANOMALY_FAILED_SPIKE_COUNT, SWARM_ANOMALY_FAILED_SPIKE_WINDOW_MS, SWARM_ANOMALY_QUEUE_STALLED_MS, SWARM_ANOMALY_PR_STUCK_MERGE_MS, SWARM_ANOMALY_PR_NO_REVIEWER_MS, SWARM_ANOMALY_QUESTION_UNANSWERED_MS, SWARM_ANOMALY_OUTBOX_STUCK_MS, SWARM_ANOMALY_RESOURCE_GATE_STALL_MS, SWARM_ANOMALY_DEP_MISSING_MS, SWARM_WATCHDOG_STALL_MS;
|
|
2537
|
+
var DB_PATH, EXECUTOR, DRONE_HOME, DRONE_HOME_ISOLATION, DRONE_ISOLATION, WORKTREES_NOINDEX_DIR, WORKTREES_LEGACY_DIR, WORKTREES_DIR, RUN_LOGS_DIR, DAEMON_LOG_PATH, DAEMON_LOG_MAX_BYTES, DAEMON_LOG_KEEP, DAEMON_LOG_CHECK_MS, DIST_BUILD, SWARM_BACKEND, SWARM_OUTBOX, SWARM_MAX_IN_FLIGHT, SWARM_CLAIM_INTERVAL_MS, SWARM_MAX_LOAD_FACTOR, SWARM_MIN_FREE_GB, SWARM_MAX_FD_LOAD_FACTOR, SWARM_MAX_CPU_BUSY, SWARM_CPU_WINDOW_MS, SWARM_CPU_SANITY_BUSY, SWARM_CPU_SANITY_LOAD_FACTOR, SWARM_CPU_SANITY_LAG_MS, SWARM_RELIEF_LOAD_FACTOR, SWARM_RELIEF_MIN_FREE_GB, SWARM_RELIEF_MAX_FD_LOAD_FACTOR, SWARM_RELIEF_CPU_BUSY, SWARM_RELIEF_COST_WINDOW_MS, SWARM_RELIEF_ALERT_PAUSES, SWARM_RELIEF_ALERT_LOST_MS, SWARM_RELIEF_ALERT_MIN_STEPS, SWARM_RELIEF_ALERT_SUSTAIN_MS, SWARM_LOW_DISK_GB, SWARM_REHYDRATE_MS, SWARM_DRONE_BRIDGE_MS, SWARM_PROD_HEARTBEAT_MS, SWARM_VERSION_CHECK_MS, SWARM_SWEEP_INTERVAL_MS, SWARM_WORKTREE_REAP_INTERVAL_MS, DRONE_CHANNEL_ENABLED, DRONE_CHANNEL_PORT, DRONE_CHANNEL_URL, DRONE_CHANNEL_SECRET_PATH, DRONE_CHANNEL_GRACE_MS, HEARTBEAT_PATH, HEARTBEAT_STALE_MS, SWARM_REPO, SWARM_SCAN_DEPTH, SWARM_REPO_PATHS, SWARM_SCAN_ROOTS_RAW, REPO_MAP_PATH, SWARM_REPO_MAP_MS, SWARM_REPO_SCAN_TTL_MS, TASKLESS_API, SWARM_SHOT_TIMEOUT_MS, TASKLESS_MCP_PATH, TASKLESS_REST_PATH, TASKLESS_TOKEN, TASKLESS_WORKSPACE, TASKLESS_WORKSPACE_SLUG, TASKLESS_TASK_URL_BASE, SWARM_VERIFY, SWARM_AUTO_REVIEW, SWARM_AUTO_VERIFY, SWARM_MERGE_QUEUE, REVIEW_CUTOFF_PATH, SWARM_REVIEW_CAP, SWARM_STORM_URL, WEB_PORT, WEB_REDIRECT_URI, SWARM_ANOMALY_RENOTIFY_MS, SWARM_ANOMALY_MAX_REPEATS, SWARM_ANOMALY_RUN_DOWN_WINDOW_MS, SWARM_ANOMALY_FAILED_SPIKE_COUNT, SWARM_ANOMALY_FAILED_SPIKE_WINDOW_MS, SWARM_ANOMALY_QUEUE_STALLED_MS, SWARM_ANOMALY_PR_STUCK_MERGE_MS, SWARM_ANOMALY_PR_NO_REVIEWER_MS, SWARM_ANOMALY_QUESTION_UNANSWERED_MS, SWARM_ANOMALY_OUTBOX_STUCK_MS, SWARM_ANOMALY_RESOURCE_GATE_STALL_MS, SWARM_ANOMALY_DEP_MISSING_MS, SWARM_WATCHDOG_STALL_MS;
|
|
2396
2538
|
var init_config = __esm({
|
|
2397
2539
|
"src/config.ts"() {
|
|
2398
2540
|
"use strict";
|
|
@@ -2400,22 +2542,22 @@ var init_config = __esm({
|
|
|
2400
2542
|
init_executors();
|
|
2401
2543
|
init_swarmHome();
|
|
2402
2544
|
init_swarmHome();
|
|
2403
|
-
DB_PATH = process.env.SWARM_DB ||
|
|
2545
|
+
DB_PATH = process.env.SWARM_DB || join6(SWARM_HOME, "swarm.db");
|
|
2404
2546
|
EXECUTOR = resolveExecutor(process.env.SWARM_EXECUTOR);
|
|
2405
|
-
DRONE_HOME = process.env.SWARM_DRONE_HOME ||
|
|
2547
|
+
DRONE_HOME = process.env.SWARM_DRONE_HOME || join6(SWARM_HOME, "drone-home");
|
|
2406
2548
|
DRONE_HOME_ISOLATION = process.env.SWARM_DRONE_ISOLATE_HOME !== "0" && process.platform !== "win32";
|
|
2407
2549
|
DRONE_ISOLATION = DRONE_HOME_ISOLATION ? { home: DRONE_HOME, swarmHome: SWARM_HOME } : void 0;
|
|
2408
|
-
WORKTREES_NOINDEX_DIR =
|
|
2409
|
-
WORKTREES_LEGACY_DIR =
|
|
2550
|
+
WORKTREES_NOINDEX_DIR = join6(SWARM_HOME, "worktrees.noindex");
|
|
2551
|
+
WORKTREES_LEGACY_DIR = join6(SWARM_HOME, "worktrees");
|
|
2410
2552
|
WORKTREES_DIR = process.env.SWARM_WORKTREES || WORKTREES_NOINDEX_DIR;
|
|
2411
|
-
RUN_LOGS_DIR = process.env.SWARM_RUN_LOGS ||
|
|
2412
|
-
DAEMON_LOG_PATH = process.env.SWARM_DAEMON_LOG ||
|
|
2553
|
+
RUN_LOGS_DIR = process.env.SWARM_RUN_LOGS || join6(SWARM_HOME, "run-logs");
|
|
2554
|
+
DAEMON_LOG_PATH = process.env.SWARM_DAEMON_LOG || join6(SWARM_HOME, "overmind.log");
|
|
2413
2555
|
DAEMON_LOG_MAX_BYTES = envNum("SWARM_DAEMON_LOG_MAX_BYTES", 16 * 1024 * 1024);
|
|
2414
2556
|
DAEMON_LOG_KEEP = envNum("SWARM_DAEMON_LOG_KEEP", 2);
|
|
2415
2557
|
DAEMON_LOG_CHECK_MS = envNum("SWARM_DAEMON_LOG_CHECK_MS", 6e4);
|
|
2416
2558
|
DIST_BUILD = true;
|
|
2417
2559
|
SWARM_BACKEND = process.env.SWARM_BACKEND === "client" ? "client" : process.env.SWARM_BACKEND === "sqlite" ? "sqlite" : DIST_BUILD ? "client" : "sqlite";
|
|
2418
|
-
SWARM_OUTBOX = process.env.SWARM_OUTBOX ||
|
|
2560
|
+
SWARM_OUTBOX = process.env.SWARM_OUTBOX || join6(SWARM_HOME, "outbox.jsonl");
|
|
2419
2561
|
SWARM_MAX_IN_FLIGHT = Math.trunc(envNum("SWARM_MAX_IN_FLIGHT", 3));
|
|
2420
2562
|
SWARM_CLAIM_INTERVAL_MS = envNum("SWARM_CLAIM_INTERVAL_MS", 6e4);
|
|
2421
2563
|
SWARM_MAX_LOAD_FACTOR = envNum("SWARM_MAX_LOAD_FACTOR", 0.9);
|
|
@@ -2445,20 +2587,21 @@ var init_config = __esm({
|
|
|
2445
2587
|
DRONE_CHANNEL_ENABLED = process.env.SWARM_DRONE_CHANNEL !== "0";
|
|
2446
2588
|
DRONE_CHANNEL_PORT = Number(process.env.SWARM_DRONE_CHANNEL_PORT) || 7421;
|
|
2447
2589
|
DRONE_CHANNEL_URL = process.env.SWARM_DRONE_CHANNEL_URL || `http://127.0.0.1:${DRONE_CHANNEL_PORT}`;
|
|
2448
|
-
DRONE_CHANNEL_SECRET_PATH = process.env.SWARM_DRONE_CHANNEL_SECRET ||
|
|
2590
|
+
DRONE_CHANNEL_SECRET_PATH = process.env.SWARM_DRONE_CHANNEL_SECRET || join6(SWARM_HOME, "drone-channel.key");
|
|
2449
2591
|
DRONE_CHANNEL_GRACE_MS = envNum("SWARM_DRONE_CHANNEL_GRACE_MS", 12e4);
|
|
2450
|
-
HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT ||
|
|
2592
|
+
HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT || join6(SWARM_HOME, "overmind.heartbeat");
|
|
2451
2593
|
HEARTBEAT_STALE_MS = Number(process.env.SWARM_HEARTBEAT_STALE_MS) || 3e4;
|
|
2452
2594
|
SWARM_REPO = process.env.SWARM_REPO || "";
|
|
2453
2595
|
SWARM_SCAN_DEPTH = Number(process.env.SWARM_SCAN_DEPTH) || 3;
|
|
2454
2596
|
SWARM_REPO_PATHS = (process.env.SWARM_REPOS || SWARM_REPO).split(",").map((s) => s.trim()).filter(Boolean);
|
|
2455
2597
|
SWARM_SCAN_ROOTS_RAW = process.env.SWARM_SCAN_ROOTS || "";
|
|
2456
|
-
REPO_MAP_PATH = process.env.SWARM_REPO_MAP ||
|
|
2598
|
+
REPO_MAP_PATH = process.env.SWARM_REPO_MAP || join6(SWARM_HOME, "repo-map.json");
|
|
2457
2599
|
SWARM_REPO_MAP_MS = envNum("SWARM_REPO_MAP_MS", 15 * 6e4);
|
|
2458
2600
|
SWARM_REPO_SCAN_TTL_MS = envNum("SWARM_REPO_SCAN_TTL_MS", 24 * 60 * 6e4);
|
|
2459
2601
|
TASKLESS_API = process.env.TASKLESS_API || "https://taskless.ru";
|
|
2460
2602
|
SWARM_SHOT_TIMEOUT_MS = envNum("SWARM_SHOT_TIMEOUT_MS", 2e4);
|
|
2461
2603
|
TASKLESS_MCP_PATH = process.env.TASKLESS_MCP_PATH || "/api/mcp";
|
|
2604
|
+
TASKLESS_REST_PATH = process.env.TASKLESS_REST_PATH || "/api";
|
|
2462
2605
|
TASKLESS_TOKEN = process.env.TASKLESS_TOKEN || tokenFromFile();
|
|
2463
2606
|
TASKLESS_WORKSPACE = process.env.TASKLESS_WORKSPACE || "";
|
|
2464
2607
|
TASKLESS_WORKSPACE_SLUG = process.env.TASKLESS_WORKSPACE_SLUG || "";
|
|
@@ -2467,7 +2610,7 @@ var init_config = __esm({
|
|
|
2467
2610
|
SWARM_AUTO_REVIEW = process.env.SWARM_AUTO_REVIEW !== "0";
|
|
2468
2611
|
SWARM_AUTO_VERIFY = process.env.SWARM_AUTO_VERIFY !== "0";
|
|
2469
2612
|
SWARM_MERGE_QUEUE = process.env.SWARM_MERGE_QUEUE !== "0";
|
|
2470
|
-
REVIEW_CUTOFF_PATH = process.env.SWARM_REVIEW_CUTOFF ||
|
|
2613
|
+
REVIEW_CUTOFF_PATH = process.env.SWARM_REVIEW_CUTOFF || join6(SWARM_HOME, "review-cutoff.json");
|
|
2471
2614
|
SWARM_REVIEW_CAP = Math.trunc(envNum("SWARM_REVIEW_CAP", 1));
|
|
2472
2615
|
SWARM_STORM_URL = process.env.SWARM_STORM_URL || "";
|
|
2473
2616
|
WEB_PORT = Number(process.env.SWARM_WEB_PORT) || 7420;
|
|
@@ -2507,7 +2650,7 @@ var init_quietExec = __esm({
|
|
|
2507
2650
|
|
|
2508
2651
|
// src/resources.ts
|
|
2509
2652
|
import os from "os";
|
|
2510
|
-
import { readFileSync as
|
|
2653
|
+
import { readFileSync as readFileSync4, statfsSync } from "fs";
|
|
2511
2654
|
function isTransientResourceError(e) {
|
|
2512
2655
|
const code = e?.code;
|
|
2513
2656
|
return code === "ENFILE" || code === "EMFILE" || code === "EAGAIN";
|
|
@@ -2565,7 +2708,7 @@ function fileDescriptorLoadRatio() {
|
|
|
2565
2708
|
}
|
|
2566
2709
|
if (process.platform === "linux") {
|
|
2567
2710
|
try {
|
|
2568
|
-
const [allocated, , max] =
|
|
2711
|
+
const [allocated, , max] = readFileSync4("/proc/sys/fs/file-nr", "utf8").trim().split(/\s+/).map(Number);
|
|
2569
2712
|
if (Number.isFinite(allocated ?? NaN) && Number.isFinite(max ?? NaN) && max) return allocated / max;
|
|
2570
2713
|
} catch {
|
|
2571
2714
|
}
|
|
@@ -2882,15 +3025,15 @@ var init_storm = __esm({
|
|
|
2882
3025
|
});
|
|
2883
3026
|
|
|
2884
3027
|
// src/heartbeat.ts
|
|
2885
|
-
import { readFileSync as
|
|
3028
|
+
import { readFileSync as readFileSync6, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
2886
3029
|
function writeHeartbeat(path, now) {
|
|
2887
3030
|
const tmp = `${path}.tmp`;
|
|
2888
|
-
|
|
3031
|
+
writeFileSync6(tmp, String(now));
|
|
2889
3032
|
renameSync4(tmp, path);
|
|
2890
3033
|
}
|
|
2891
3034
|
function readHeartbeat(path) {
|
|
2892
3035
|
try {
|
|
2893
|
-
const ms = Number(
|
|
3036
|
+
const ms = Number(readFileSync6(path, "utf8").trim());
|
|
2894
3037
|
return Number.isFinite(ms) ? ms : null;
|
|
2895
3038
|
} catch {
|
|
2896
3039
|
return null;
|
|
@@ -2946,11 +3089,11 @@ var init_logNoise = __esm({
|
|
|
2946
3089
|
});
|
|
2947
3090
|
|
|
2948
3091
|
// src/agentToken.ts
|
|
2949
|
-
import { readFileSync as
|
|
2950
|
-
import { join as
|
|
3092
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, unlinkSync as unlinkSync3 } from "fs";
|
|
3093
|
+
import { join as join9 } from "path";
|
|
2951
3094
|
function loadAgentToken() {
|
|
2952
3095
|
try {
|
|
2953
|
-
const s = JSON.parse(
|
|
3096
|
+
const s = JSON.parse(readFileSync7(STORE_PATH, "utf8"));
|
|
2954
3097
|
return s.token ? s : null;
|
|
2955
3098
|
} catch {
|
|
2956
3099
|
return null;
|
|
@@ -3003,7 +3146,7 @@ var init_agentToken = __esm({
|
|
|
3003
3146
|
"src/agentToken.ts"() {
|
|
3004
3147
|
"use strict";
|
|
3005
3148
|
init_config();
|
|
3006
|
-
STORE_PATH =
|
|
3149
|
+
STORE_PATH = join9(SWARM_HOME, "agent.json");
|
|
3007
3150
|
base = (api) => api.replace(/\/$/, "");
|
|
3008
3151
|
TOKEN_PROBE_TIMEOUT_MS = 8e3;
|
|
3009
3152
|
}
|
|
@@ -3441,9 +3584,9 @@ var init_netTimeout = __esm({
|
|
|
3441
3584
|
});
|
|
3442
3585
|
|
|
3443
3586
|
// src/git.ts
|
|
3444
|
-
import { existsSync as
|
|
3587
|
+
import { existsSync as existsSync11, readdirSync as readdirSync11, statSync as statSync8 } from "fs";
|
|
3445
3588
|
import { appendFile, chmod, mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
3446
|
-
import { isAbsolute as isAbsolute2, join as
|
|
3589
|
+
import { isAbsolute as isAbsolute2, join as join18, sep as sep2 } from "path";
|
|
3447
3590
|
import { $ as $2 } from "execa";
|
|
3448
3591
|
function gitNet(repo, args, timeoutMs) {
|
|
3449
3592
|
return withHardTimeout(
|
|
@@ -3466,7 +3609,7 @@ async function reclaimStaleWorktree(repo, worktree) {
|
|
|
3466
3609
|
}
|
|
3467
3610
|
function isLinkedWorktree(dir) {
|
|
3468
3611
|
try {
|
|
3469
|
-
return statSync8(
|
|
3612
|
+
return statSync8(join18(dir, ".git")).isFile();
|
|
3470
3613
|
} catch {
|
|
3471
3614
|
return false;
|
|
3472
3615
|
}
|
|
@@ -3478,7 +3621,7 @@ async function detachWorktree(repo, worktree) {
|
|
|
3478
3621
|
}
|
|
3479
3622
|
}
|
|
3480
3623
|
async function ensureWorktreeDirGone(worktree) {
|
|
3481
|
-
if (!
|
|
3624
|
+
if (!existsSync11(worktree)) return false;
|
|
3482
3625
|
if (!isLinkedWorktree(worktree)) {
|
|
3483
3626
|
console.error(
|
|
3484
3627
|
`[git] ${worktree} \u2014 \u043D\u0435 \u043F\u0440\u0438\u0441\u0442\u0451\u0433\u043D\u0443\u0442\u044B\u0439 worktree (.git \u043D\u0435 \u0444\u0430\u0439\u043B): \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u041D\u0415 \u0442\u0440\u043E\u0433\u0430\u044E. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u044C\u043D\u043E worktree \u0434\u0440\u043E\u043D\u0430 \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435 \u0435\u0433\u043E \u0440\u0443\u043A\u0430\u043C\u0438; \u0435\u0441\u043B\u0438 \u044D\u0442\u043E \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u0447\u0435\u043A\u0430\u0443\u0442, \u0437\u043D\u0430\u0447\u0438\u0442 \u0432\u0435\u0442\u043A\u0430 \u0441\u043B\u0430\u0433\u0430 \u0447\u0435\u043A\u043D\u0443\u0442\u0430 \u0432 \u043D\u0451\u043C (git checkout <slug>) \u2014 \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u0435 \u0435\u0433\u043E \u043D\u0430 \u0434\u0440\u0443\u0433\u0443\u044E \u0432\u0435\u0442\u043A\u0443.`
|
|
@@ -3493,7 +3636,7 @@ async function ensureWorktreeDirGone(worktree) {
|
|
|
3493
3636
|
);
|
|
3494
3637
|
return true;
|
|
3495
3638
|
}
|
|
3496
|
-
if (
|
|
3639
|
+
if (existsSync11(worktree)) {
|
|
3497
3640
|
console.error(`[git] \u043A\u0430\u0442\u0430\u043B\u043E\u0433 worktree ${worktree} \u043F\u0435\u0440\u0435\u0436\u0438\u043B rm \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435 \u0440\u0443\u043A\u0430\u043C\u0438, \u0438\u043D\u0430\u0447\u0435 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0441\u043F\u0430\u0432\u043D \u0441\u043B\u0430\u0433\u0430 \u0432\u0441\u0442\u0430\u043D\u0435\u0442.`);
|
|
3498
3641
|
}
|
|
3499
3642
|
return true;
|
|
@@ -3541,7 +3684,7 @@ async function checkoutWorktree(repo, path, branch, opts) {
|
|
|
3541
3684
|
}
|
|
3542
3685
|
const busyAt = await worktreeForBranch(repo, branch);
|
|
3543
3686
|
if (busyAt) {
|
|
3544
|
-
const exists =
|
|
3687
|
+
const exists = existsSync11(busyAt);
|
|
3545
3688
|
const live = exists && (opts.isWorktreeHeldLive ? await opts.isWorktreeHeldLive(busyAt) : true);
|
|
3546
3689
|
if (live) throw new WorktreeBusyError(branch, busyAt);
|
|
3547
3690
|
if (exists && await worktreeDirty(busyAt)) throw new WorktreeBusyError(branch, busyAt);
|
|
@@ -3576,8 +3719,8 @@ async function ensureGitExcluded(worktree, pattern) {
|
|
|
3576
3719
|
try {
|
|
3577
3720
|
const commonDir = await gitPath(worktree, "--git-common-dir");
|
|
3578
3721
|
if (!commonDir) return false;
|
|
3579
|
-
const infoDir =
|
|
3580
|
-
const excludeFile =
|
|
3722
|
+
const infoDir = join18(commonDir, "info");
|
|
3723
|
+
const excludeFile = join18(infoDir, "exclude");
|
|
3581
3724
|
let current = "";
|
|
3582
3725
|
try {
|
|
3583
3726
|
current = await readFile(excludeFile, "utf8");
|
|
@@ -3597,7 +3740,7 @@ async function gitPath(worktree, flag) {
|
|
|
3597
3740
|
const out = await $2({ reject: false })`git -C ${worktree} rev-parse ${flag}`;
|
|
3598
3741
|
const raw = out.stdout.trim();
|
|
3599
3742
|
if (out.exitCode !== 0 || !raw) return null;
|
|
3600
|
-
return isAbsolute2(raw) ? raw :
|
|
3743
|
+
return isAbsolute2(raw) ? raw : join18(worktree, raw);
|
|
3601
3744
|
}
|
|
3602
3745
|
async function ensureWorktreeHygiene(worktree) {
|
|
3603
3746
|
for (const pattern of SWARM_EXCLUDE_PATTERNS) await ensureGitExcluded(worktree, pattern);
|
|
@@ -3607,12 +3750,12 @@ async function installGuardHooks(worktree) {
|
|
|
3607
3750
|
try {
|
|
3608
3751
|
const gitDir = await gitPath(worktree, "--git-dir");
|
|
3609
3752
|
if (!gitDir) return false;
|
|
3610
|
-
const hooksDir =
|
|
3753
|
+
const hooksDir = join18(gitDir, "swarm-hooks");
|
|
3611
3754
|
const prev = await previousHooksPath(worktree);
|
|
3612
3755
|
await mkdir(hooksDir, { recursive: true });
|
|
3613
3756
|
for (const hook of GUARD_HOOKS) {
|
|
3614
|
-
await writeFile(
|
|
3615
|
-
await chmod(
|
|
3757
|
+
await writeFile(join18(hooksDir, hook), guardHookScript(hook, prev ? `${posix(prev)}/${hook}` : null));
|
|
3758
|
+
await chmod(join18(hooksDir, hook), 493);
|
|
3616
3759
|
}
|
|
3617
3760
|
await $2`git -C ${worktree} config extensions.worktreeConfig true`;
|
|
3618
3761
|
await $2`git -C ${worktree} config --worktree core.hooksPath ${posix(hooksDir)}`;
|
|
@@ -3631,7 +3774,7 @@ async function previousHooksPath(worktree) {
|
|
|
3631
3774
|
if (out.exitCode === 0 && value) return value;
|
|
3632
3775
|
}
|
|
3633
3776
|
const commonDir = await gitPath(worktree, "--git-common-dir");
|
|
3634
|
-
return commonDir ?
|
|
3777
|
+
return commonDir ? join18(commonDir, "hooks") : null;
|
|
3635
3778
|
}
|
|
3636
3779
|
async function removeWorktree(repo, path) {
|
|
3637
3780
|
const branch = (await $2({ reject: false })`git -C ${path} symbolic-ref --quiet --short HEAD`).stdout.trim();
|
|
@@ -3656,7 +3799,7 @@ async function worktreeDirty(path) {
|
|
|
3656
3799
|
async function repairMovedWorktrees(targetDir) {
|
|
3657
3800
|
const subdirs = (dir) => {
|
|
3658
3801
|
try {
|
|
3659
|
-
return readdirSync11(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) =>
|
|
3802
|
+
return readdirSync11(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join18(dir, d.name));
|
|
3660
3803
|
} catch {
|
|
3661
3804
|
return [];
|
|
3662
3805
|
}
|
|
@@ -3717,7 +3860,7 @@ async function mergeRef(worktree, ref) {
|
|
|
3717
3860
|
return { clean: false, conflicts: u ? u.split("\n") : [], changed: false };
|
|
3718
3861
|
}
|
|
3719
3862
|
async function pushHeadTo(worktree, branch) {
|
|
3720
|
-
await gitNetStrict(worktree, ["push", "origin", `HEAD
|
|
3863
|
+
await gitNetStrict(worktree, ["push", "origin", `HEAD:refs/heads/${branch}`], GIT_HEAVY_NET_TIMEOUT_MS);
|
|
3721
3864
|
}
|
|
3722
3865
|
async function snapshotWorktree(worktree, message) {
|
|
3723
3866
|
try {
|
|
@@ -3785,20 +3928,20 @@ __export(droneChannel_exports, {
|
|
|
3785
3928
|
verifyLocalCliToken: () => verifyLocalCliToken
|
|
3786
3929
|
});
|
|
3787
3930
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
3788
|
-
import { chmodSync, mkdirSync as
|
|
3789
|
-
import { dirname as
|
|
3931
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "fs";
|
|
3932
|
+
import { dirname as dirname4 } from "path";
|
|
3790
3933
|
import { serve } from "@hono/node-server";
|
|
3791
3934
|
function loadChannelSecret(path) {
|
|
3792
3935
|
try {
|
|
3793
|
-
const s =
|
|
3936
|
+
const s = readFileSync15(path, "utf8").trim();
|
|
3794
3937
|
if (s.length >= 32) return s;
|
|
3795
3938
|
} catch {
|
|
3796
3939
|
}
|
|
3797
3940
|
const secret = randomBytes(32).toString("hex");
|
|
3798
|
-
|
|
3799
|
-
|
|
3941
|
+
mkdirSync9(dirname4(path), { recursive: true });
|
|
3942
|
+
writeFileSync11(path, secret, { mode: 384 });
|
|
3800
3943
|
try {
|
|
3801
|
-
|
|
3944
|
+
chmodSync2(path, 384);
|
|
3802
3945
|
} catch {
|
|
3803
3946
|
}
|
|
3804
3947
|
return secret;
|
|
@@ -3856,6 +3999,12 @@ function invalidPatchValue(field, v) {
|
|
|
3856
3999
|
return typeof v === "number" && Number.isInteger(v) && v >= 0 ? null : `\u043D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u044B\u0439 \u0446\u0438\u043A\u043B \u0440\u0435\u0432\u044C\u044E: ${String(v)}`;
|
|
3857
4000
|
case "pr_url":
|
|
3858
4001
|
return v === null || typeof v === "string" ? null : "pr_url \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u043E\u0439";
|
|
4002
|
+
case "changed_paths": {
|
|
4003
|
+
if (!Array.isArray(v)) return "changed_paths \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043C\u0430\u0441\u0441\u0438\u0432\u043E\u043C \u043F\u0443\u0442\u0435\u0439";
|
|
4004
|
+
if (v.length > CHANGED_PATHS_MAX) return `changed_paths: \u0441\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u043D\u043E\u0433\u043E \u043F\u0443\u0442\u0435\u0439 (${v.length} > ${CHANGED_PATHS_MAX})`;
|
|
4005
|
+
const badPath = v.find((p) => typeof p !== "string" || !p || p.length > CHANGED_PATH_LEN_MAX);
|
|
4006
|
+
return badPath === void 0 ? null : `changed_paths: \u043D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u044B\u0439 \u043F\u0443\u0442\u044C: ${String(badPath).slice(0, 80)}`;
|
|
4007
|
+
}
|
|
3859
4008
|
default:
|
|
3860
4009
|
return `\u043F\u043E\u043B\u0435 \xAB${field}\xBB \u043A\u0430\u043D\u0430\u043B\u0443 \u0434\u0440\u043E\u043D\u0430 \u043D\u0435 \u043E\u0442\u0434\u0430\u0451\u0442\u0441\u044F`;
|
|
3861
4010
|
}
|
|
@@ -4109,7 +4258,7 @@ function channelDroneStore(opts) {
|
|
|
4109
4258
|
openQuestionsFor: async () => (await call("openQuestionsFor")).questions
|
|
4110
4259
|
};
|
|
4111
4260
|
}
|
|
4112
|
-
var LOCAL_CLI_KEY, PATCHABLE, EVENT_KIND_SET, ChannelUnavailableError, BACKOFF_MS;
|
|
4261
|
+
var LOCAL_CLI_KEY, PATCHABLE, CHANGED_PATHS_MAX, CHANGED_PATH_LEN_MAX, EVENT_KIND_SET, ChannelUnavailableError, BACKOFF_MS;
|
|
4113
4262
|
var init_droneChannel = __esm({
|
|
4114
4263
|
"src/droneChannel.ts"() {
|
|
4115
4264
|
"use strict";
|
|
@@ -4123,8 +4272,16 @@ var init_droneChannel = __esm({
|
|
|
4123
4272
|
// и пишется он тем же путём на ПРОВЕРЯЕМЫЙ прогон. Без него роль verifier была немой:
|
|
4124
4273
|
// вердикт выносился комментарием на PR, а `done` отбивался каналом.
|
|
4125
4274
|
"verify_verdict",
|
|
4126
|
-
"review_cycle"
|
|
4275
|
+
"review_cycle",
|
|
4276
|
+
// TASWM-506: файлы, которые прогон занял своим PR. Едут тем же вызовом, что и сдача
|
|
4277
|
+
// (done → status=review + pr_url): считает их НАШ код в дрон-сервере (`changedPaths` по
|
|
4278
|
+
// worktree), а не модель — со стороны канала это такой же самоотчёт прогона о себе, как
|
|
4279
|
+
// pr_url. Потолки на размер — ниже, в invalidPatchValue: список идёт в гейт раздачи, и
|
|
4280
|
+
// сорвавшийся дрон не должен уметь заявить занятым весь репозиторий.
|
|
4281
|
+
"changed_paths"
|
|
4127
4282
|
]);
|
|
4283
|
+
CHANGED_PATHS_MAX = 1e3;
|
|
4284
|
+
CHANGED_PATH_LEN_MAX = 1024;
|
|
4128
4285
|
EVENT_KIND_SET = new Set(EVENT_KINDS);
|
|
4129
4286
|
ChannelUnavailableError = class extends Error {
|
|
4130
4287
|
constructor(downMs, message) {
|
|
@@ -4140,7 +4297,7 @@ var init_droneChannel = __esm({
|
|
|
4140
4297
|
|
|
4141
4298
|
// src/attachments.ts
|
|
4142
4299
|
import { mkdir as mkdir2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
4143
|
-
import { join as
|
|
4300
|
+
import { join as join21 } from "path";
|
|
4144
4301
|
function attachmentsFromDoc(doc, origin) {
|
|
4145
4302
|
const out = [];
|
|
4146
4303
|
const walk2 = (node) => {
|
|
@@ -4231,7 +4388,7 @@ async function materializeAttachments(opts) {
|
|
|
4231
4388
|
};
|
|
4232
4389
|
const result = { files: [], skipped: [] };
|
|
4233
4390
|
if (opts.refs.length === 0) return result;
|
|
4234
|
-
const dir =
|
|
4391
|
+
const dir = join21(opts.worktree, ATTACHMENTS_DIR);
|
|
4235
4392
|
await io.removeDir(dir);
|
|
4236
4393
|
let dirReady = false;
|
|
4237
4394
|
let total = 0;
|
|
@@ -4277,7 +4434,7 @@ async function materializeAttachments(opts) {
|
|
|
4277
4434
|
await io.mkdir(dir);
|
|
4278
4435
|
dirReady = true;
|
|
4279
4436
|
}
|
|
4280
|
-
await io.writeFile(
|
|
4437
|
+
await io.writeFile(join21(dir, name), file.bytes);
|
|
4281
4438
|
} catch (e) {
|
|
4282
4439
|
result.skipped.push(`${label2} \u2014 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B\u043E\u0441\u044C \u043D\u0430 \u0434\u0438\u0441\u043A (${errText(e)})`);
|
|
4283
4440
|
continue;
|
|
@@ -4430,7 +4587,7 @@ var init_attachments = __esm({
|
|
|
4430
4587
|
|
|
4431
4588
|
// src/shotUpload.ts
|
|
4432
4589
|
import { readFile as fsReadFile } from "fs/promises";
|
|
4433
|
-
import { basename as basename4, isAbsolute as isAbsolute3, join as
|
|
4590
|
+
import { basename as basename4, isAbsolute as isAbsolute3, join as join22 } from "path";
|
|
4434
4591
|
function shotMimetype(path) {
|
|
4435
4592
|
const dot = path.lastIndexOf(".");
|
|
4436
4593
|
if (dot < 0) return null;
|
|
@@ -4456,7 +4613,7 @@ async function readShots(paths, opts) {
|
|
|
4456
4613
|
}
|
|
4457
4614
|
let bytes;
|
|
4458
4615
|
try {
|
|
4459
|
-
bytes = await read(isAbsolute3(path) ? path :
|
|
4616
|
+
bytes = await read(isAbsolute3(path) ? path : join22(opts.worktree, path));
|
|
4460
4617
|
} catch (e) {
|
|
4461
4618
|
skipped.push({ path, reason: `\u043D\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D: ${e instanceof Error ? e.message : String(e)}` });
|
|
4462
4619
|
continue;
|
|
@@ -4466,7 +4623,10 @@ async function readShots(paths, opts) {
|
|
|
4466
4623
|
continue;
|
|
4467
4624
|
}
|
|
4468
4625
|
if (bytes.byteLength > maxBytes) {
|
|
4469
|
-
skipped.push({
|
|
4626
|
+
skipped.push({
|
|
4627
|
+
path,
|
|
4628
|
+
reason: `${(bytes.byteLength / 1024 / 1024).toFixed(1)} \u041C\u0411 \u2014 \u0431\u043E\u043B\u044C\u0448\u0435 \u043B\u0438\u043C\u0438\u0442\u0430 ${maxBytes / 1024 / 1024} \u041C\u0411; \u043F\u0435\u0440\u0435\u0441\u043D\u0438\u043C\u0438 \u043F\u043E\u043B\u0435\u0433\u0447\u0435: swarm shot \u2026 --scale 1 (\u0438\u043B\u0438 --scroll-to <\u0441\u0435\u043B\u0435\u043A\u0442\u043E\u0440> \u0432\u043C\u0435\u0441\u0442\u043E \u0434\u043B\u0438\u043D\u043D\u043E\u0433\u043E \u043A\u0430\u0434\u0440\u0430)`
|
|
4629
|
+
});
|
|
4470
4630
|
continue;
|
|
4471
4631
|
}
|
|
4472
4632
|
files.push({ path, fileName: basename4(path), mimetype, bytes });
|
|
@@ -4521,7 +4681,7 @@ var init_shotUpload = __esm({
|
|
|
4521
4681
|
"src/shotUpload.ts"() {
|
|
4522
4682
|
"use strict";
|
|
4523
4683
|
SHOT_MAX_FILES = 6;
|
|
4524
|
-
SHOT_MAX_FILE_BYTES =
|
|
4684
|
+
SHOT_MAX_FILE_BYTES = 20 * 1024 * 1024;
|
|
4525
4685
|
MIME_BY_EXT = {
|
|
4526
4686
|
".png": "image/png",
|
|
4527
4687
|
".jpg": "image/jpeg",
|
|
@@ -4534,8 +4694,8 @@ var init_shotUpload = __esm({
|
|
|
4534
4694
|
|
|
4535
4695
|
// src/oauth.ts
|
|
4536
4696
|
import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
|
|
4537
|
-
import { readFileSync as
|
|
4538
|
-
import { join as
|
|
4697
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, unlinkSync as unlinkSync4 } from "fs";
|
|
4698
|
+
import { join as join23 } from "path";
|
|
4539
4699
|
function makePkce() {
|
|
4540
4700
|
const verifier = randomBytes2(48).toString("base64url");
|
|
4541
4701
|
const challenge = createHash2("sha256").update(verifier).digest("base64url");
|
|
@@ -4619,13 +4779,13 @@ function toStore(clientId, t, now) {
|
|
|
4619
4779
|
}
|
|
4620
4780
|
function loadStore() {
|
|
4621
4781
|
try {
|
|
4622
|
-
return JSON.parse(
|
|
4782
|
+
return JSON.parse(readFileSync16(STORE_PATH2, "utf8"));
|
|
4623
4783
|
} catch {
|
|
4624
4784
|
return null;
|
|
4625
4785
|
}
|
|
4626
4786
|
}
|
|
4627
4787
|
function saveStore(s) {
|
|
4628
|
-
|
|
4788
|
+
writeFileSync12(STORE_PATH2, JSON.stringify(s, null, 2), { mode: 384 });
|
|
4629
4789
|
}
|
|
4630
4790
|
function clearStore() {
|
|
4631
4791
|
try {
|
|
@@ -4635,7 +4795,7 @@ function clearStore() {
|
|
|
4635
4795
|
}
|
|
4636
4796
|
function loadClientId() {
|
|
4637
4797
|
try {
|
|
4638
|
-
return JSON.parse(
|
|
4798
|
+
return JSON.parse(readFileSync16(CLIENT_PATH, "utf8")).clientId;
|
|
4639
4799
|
} catch {
|
|
4640
4800
|
return null;
|
|
4641
4801
|
}
|
|
@@ -4644,7 +4804,7 @@ async function ensureClientId(api, redirectUri, fetchFn = fetch) {
|
|
|
4644
4804
|
const existing = loadClientId();
|
|
4645
4805
|
if (existing) return existing;
|
|
4646
4806
|
const clientId = await registerClient({ api, redirectUri }, fetchFn);
|
|
4647
|
-
|
|
4807
|
+
writeFileSync12(CLIENT_PATH, JSON.stringify({ clientId }, null, 2), { mode: 384 });
|
|
4648
4808
|
return clientId;
|
|
4649
4809
|
}
|
|
4650
4810
|
async function ensureAccess(store, opts = {}) {
|
|
@@ -4670,8 +4830,8 @@ var init_oauth = __esm({
|
|
|
4670
4830
|
AUTHORIZE = "/api/oauth/authorize";
|
|
4671
4831
|
TOKEN = "/api/oauth/token";
|
|
4672
4832
|
SCOPE = "openid profile email taskless:full";
|
|
4673
|
-
STORE_PATH2 =
|
|
4674
|
-
CLIENT_PATH =
|
|
4833
|
+
STORE_PATH2 = join23(SWARM_HOME, "oauth.json");
|
|
4834
|
+
CLIENT_PATH = join23(SWARM_HOME, "oauth-client.json");
|
|
4675
4835
|
base2 = (api) => api.replace(/\/$/, "");
|
|
4676
4836
|
}
|
|
4677
4837
|
});
|
|
@@ -4789,11 +4949,11 @@ var init_authState = __esm({
|
|
|
4789
4949
|
});
|
|
4790
4950
|
|
|
4791
4951
|
// src/workspace.ts
|
|
4792
|
-
import { readFileSync as
|
|
4793
|
-
import { join as
|
|
4952
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync13, unlinkSync as unlinkSync5 } from "fs";
|
|
4953
|
+
import { join as join24 } from "path";
|
|
4794
4954
|
function loadWorkspace() {
|
|
4795
4955
|
try {
|
|
4796
|
-
const w = JSON.parse(
|
|
4956
|
+
const w = JSON.parse(readFileSync17(WORKSPACE_PATH, "utf8"));
|
|
4797
4957
|
return w && typeof w.id === "string" && w.id ? w : null;
|
|
4798
4958
|
} catch {
|
|
4799
4959
|
return null;
|
|
@@ -4807,7 +4967,7 @@ var init_workspace = __esm({
|
|
|
4807
4967
|
"src/workspace.ts"() {
|
|
4808
4968
|
"use strict";
|
|
4809
4969
|
init_config();
|
|
4810
|
-
WORKSPACE_PATH =
|
|
4970
|
+
WORKSPACE_PATH = join24(SWARM_HOME, "workspace.json");
|
|
4811
4971
|
}
|
|
4812
4972
|
});
|
|
4813
4973
|
|
|
@@ -4844,7 +5004,7 @@ function isClosingTaskCategory(category) {
|
|
|
4844
5004
|
function taskAuthor(u) {
|
|
4845
5005
|
return u?.id ? { id: u.id, name: u.displayName || "\u0430\u0432\u0442\u043E\u0440 \u0437\u0430\u0434\u0430\u0447\u0438" } : null;
|
|
4846
5006
|
}
|
|
4847
|
-
function
|
|
5007
|
+
function toolErrorText2(r) {
|
|
4848
5008
|
const fromContent = (r.content ?? []).map((c) => c.text ?? "").filter(Boolean).join(" ").trim();
|
|
4849
5009
|
return fromContent || JSON.stringify(r);
|
|
4850
5010
|
}
|
|
@@ -4856,7 +5016,7 @@ function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl
|
|
|
4856
5016
|
try {
|
|
4857
5017
|
const r = await call(name, WS_FREE_TOOLS.has(name) ? args : { ...args, ...ws });
|
|
4858
5018
|
if (r?.isError) {
|
|
4859
|
-
const raw =
|
|
5019
|
+
const raw = toolErrorText2(r);
|
|
4860
5020
|
const detail = trimDetail(raw);
|
|
4861
5021
|
console.error(`[taskless] \u0442\u0443\u043B ${name} \u0432\u0435\u0440\u043D\u0443\u043B \u043E\u0448\u0438\u0431\u043A\u0443:`, detail);
|
|
4862
5022
|
return { ok: false, failure: { code: classifyToolFailure(raw), detail } };
|
|
@@ -5388,7 +5548,7 @@ __export(db_exports, {
|
|
|
5388
5548
|
});
|
|
5389
5549
|
import { createRequire as createRequire2 } from "module";
|
|
5390
5550
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5391
|
-
import { existsSync as
|
|
5551
|
+
import { existsSync as existsSync13 } from "fs";
|
|
5392
5552
|
import { resolve as resolve3 } from "path";
|
|
5393
5553
|
function rowToQuestion(row) {
|
|
5394
5554
|
return {
|
|
@@ -5432,9 +5592,14 @@ function rowToRun(row) {
|
|
|
5432
5592
|
tokens_out: row.tokens_out ?? 0,
|
|
5433
5593
|
duration_ms: row.duration_ms ?? 0,
|
|
5434
5594
|
pr_url: row.pr_url ?? null,
|
|
5595
|
+
// TASWM-506: файлы, занятые прогоном с открытым PR — JSON-массив, как depends_on.
|
|
5596
|
+
changed_paths: row.changed_paths ? JSON.parse(row.changed_paths) : [],
|
|
5435
5597
|
superseded_by: row.superseded_by ?? null,
|
|
5436
5598
|
decision: row.decision ? JSON.parse(row.decision) : null,
|
|
5437
5599
|
stop_reason: row.stop_reason ?? null,
|
|
5600
|
+
// TASWM-505: через узкий парсер, как verify_verdict ниже — в базе может лежать код,
|
|
5601
|
+
// записанный демоном другой версии; непонятное здесь значит «кода нет».
|
|
5602
|
+
return_reason: toReturnReason(row.return_reason),
|
|
5438
5603
|
respawns: row.respawns ?? 0,
|
|
5439
5604
|
// старые прогоны без колонки → 0
|
|
5440
5605
|
resolves_run_id: row.resolves_run_id ?? null,
|
|
@@ -5509,6 +5674,7 @@ CREATE TABLE IF NOT EXISTS runs (
|
|
|
5509
5674
|
superseded_by INTEGER,
|
|
5510
5675
|
decision TEXT,
|
|
5511
5676
|
stop_reason TEXT,
|
|
5677
|
+
return_reason TEXT,
|
|
5512
5678
|
respawns INTEGER NOT NULL DEFAULT 0,
|
|
5513
5679
|
resolves_run_id INTEGER,
|
|
5514
5680
|
reviews_run_id INTEGER,
|
|
@@ -5625,7 +5791,16 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
5625
5791
|
// TASWM-451: чем проверять работу этого прогона — модель ревью, приехавшая с задачи.
|
|
5626
5792
|
// Живёт на прогоне ИСПОЛНИТЕЛЯ: ревьюер и верификатор заводятся локально и читают её
|
|
5627
5793
|
// оттуда, а не ходят за задачей в прод в момент спавна.
|
|
5628
|
-
"ALTER TABLE runs ADD COLUMN review_model TEXT"
|
|
5794
|
+
"ALTER TABLE runs ADD COLUMN review_model TEXT",
|
|
5795
|
+
// TASWM-505: КОД причины возврата задачи автору (unclear_goal|no_acceptance|too_big|
|
|
5796
|
+
// contradiction) рядом с прозой в stop_reason — из него складывается распределение
|
|
5797
|
+
// «чего в постановках не хватает чаще всего». NULL — прогон не возвращали либо
|
|
5798
|
+
// возврат сделан демоном до этой фичи.
|
|
5799
|
+
"ALTER TABLE runs ADD COLUMN return_reason TEXT",
|
|
5800
|
+
// TASWM-506: файлы, которые держит прогон с открытым PR (JSON-массив, как depends_on).
|
|
5801
|
+
// Снимается в момент сдачи с PR: worktree сносится на мерже, и пересчитать список
|
|
5802
|
+
// потом неоткуда.
|
|
5803
|
+
"ALTER TABLE runs ADD COLUMN changed_paths TEXT"
|
|
5629
5804
|
]) {
|
|
5630
5805
|
try {
|
|
5631
5806
|
this.db.exec(alter);
|
|
@@ -5771,7 +5946,7 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
5771
5946
|
const set = fields.map((f) => `${f} = ?`).join(", ");
|
|
5772
5947
|
const vals = fields.map((f) => {
|
|
5773
5948
|
const v = patch[f];
|
|
5774
|
-
if (f === "depends_on") {
|
|
5949
|
+
if (f === "depends_on" || f === "changed_paths") {
|
|
5775
5950
|
const arr = v;
|
|
5776
5951
|
return arr && arr.length ? JSON.stringify(arr) : null;
|
|
5777
5952
|
}
|
|
@@ -5996,7 +6171,7 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
5996
6171
|
const repoN = normalizeRepo(repo);
|
|
5997
6172
|
const already = this.db.prepare("SELECT run_count FROM imports WHERE source = ?").get(abs);
|
|
5998
6173
|
if (already) return { imported: false, runs: already.run_count };
|
|
5999
|
-
if (!
|
|
6174
|
+
if (!existsSync13(abs)) throw new Error(`import-db: \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442: ${abs}`);
|
|
6000
6175
|
const src = new this.databaseCtor(abs);
|
|
6001
6176
|
try {
|
|
6002
6177
|
const runs = src.prepare("SELECT * FROM runs ORDER BY id").all();
|
|
@@ -6364,10 +6539,10 @@ var init_droneStore = __esm({
|
|
|
6364
6539
|
});
|
|
6365
6540
|
|
|
6366
6541
|
// src/overmind.ts
|
|
6367
|
-
import { homedir as
|
|
6368
|
-
import { existsSync as
|
|
6369
|
-
import { basename as basename6, dirname as
|
|
6370
|
-
import { fileURLToPath as
|
|
6542
|
+
import { homedir as homedir6 } from "os";
|
|
6543
|
+
import { existsSync as existsSync18, readFileSync as readFileSync21, readdirSync as readdirSync13, renameSync as renameSync11 } from "fs";
|
|
6544
|
+
import { basename as basename6, dirname as dirname5, join as join31 } from "path";
|
|
6545
|
+
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
|
|
6371
6546
|
import { spawn as spawn3 } from "child_process";
|
|
6372
6547
|
|
|
6373
6548
|
// src/restart.ts
|
|
@@ -6446,8 +6621,8 @@ init_config();
|
|
|
6446
6621
|
init_resources();
|
|
6447
6622
|
init_names2();
|
|
6448
6623
|
init_storm();
|
|
6449
|
-
import { appendFileSync, existsSync as
|
|
6450
|
-
import { dirname } from "path";
|
|
6624
|
+
import { appendFileSync, existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
6625
|
+
import { dirname as dirname2 } from "path";
|
|
6451
6626
|
import { randomUUID } from "crypto";
|
|
6452
6627
|
|
|
6453
6628
|
// src/repoRules.ts
|
|
@@ -6499,12 +6674,12 @@ function renderPendingRulesBlock(rules) {
|
|
|
6499
6674
|
}
|
|
6500
6675
|
|
|
6501
6676
|
// src/logTail.ts
|
|
6502
|
-
import { closeSync, existsSync as
|
|
6503
|
-
import { join as
|
|
6677
|
+
import { closeSync, existsSync as existsSync6, openSync, readSync, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
|
|
6678
|
+
import { join as join8 } from "path";
|
|
6504
6679
|
|
|
6505
6680
|
// src/runLogDir.ts
|
|
6506
|
-
import { existsSync as
|
|
6507
|
-
import { join as
|
|
6681
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync4, renameSync as renameSync3, statSync as statSync3 } from "fs";
|
|
6682
|
+
import { join as join7 } from "path";
|
|
6508
6683
|
var LEGACY_RUN_LOGS_SUBDIR = "legacy";
|
|
6509
6684
|
function runLogDirName(runLogKey) {
|
|
6510
6685
|
const safe2 = runLogKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^[-_]+|[-_]+$/g, "");
|
|
@@ -6512,12 +6687,12 @@ function runLogDirName(runLogKey) {
|
|
|
6512
6687
|
return /^\d+$/.test(safe2) ? `k-${safe2}` : safe2;
|
|
6513
6688
|
}
|
|
6514
6689
|
function runLogDirPath(runLogsDir, runLogKey) {
|
|
6515
|
-
return
|
|
6690
|
+
return join7(runLogsDir, runLogDirName(runLogKey));
|
|
6516
6691
|
}
|
|
6517
6692
|
function findRunLogDir(runLogsDir, keys) {
|
|
6518
6693
|
for (const key of keys) {
|
|
6519
6694
|
const dir = runLogDirPath(runLogsDir, key);
|
|
6520
|
-
if (
|
|
6695
|
+
if (existsSync5(dir)) return dir;
|
|
6521
6696
|
}
|
|
6522
6697
|
return null;
|
|
6523
6698
|
}
|
|
@@ -6527,7 +6702,7 @@ function runLogDirForWrite(runLogsDir, keys) {
|
|
|
6527
6702
|
return findRunLogDir(runLogsDir, keys) ?? runLogDirPath(runLogsDir, canonical);
|
|
6528
6703
|
}
|
|
6529
6704
|
function sweepLegacyRunLogs(runLogsDir) {
|
|
6530
|
-
const legacyDir =
|
|
6705
|
+
const legacyDir = join7(runLogsDir, LEGACY_RUN_LOGS_SUBDIR);
|
|
6531
6706
|
let entries;
|
|
6532
6707
|
try {
|
|
6533
6708
|
entries = readdirSync4(runLogsDir);
|
|
@@ -6538,12 +6713,12 @@ function sweepLegacyRunLogs(runLogsDir) {
|
|
|
6538
6713
|
let failed = 0;
|
|
6539
6714
|
for (const name of entries) {
|
|
6540
6715
|
if (!/^\d+$/.test(name)) continue;
|
|
6541
|
-
const from =
|
|
6716
|
+
const from = join7(runLogsDir, name);
|
|
6542
6717
|
try {
|
|
6543
6718
|
if (!statSync3(from).isDirectory()) continue;
|
|
6544
|
-
|
|
6545
|
-
let to =
|
|
6546
|
-
for (let n = 2;
|
|
6719
|
+
mkdirSync5(legacyDir, { recursive: true });
|
|
6720
|
+
let to = join7(legacyDir, name);
|
|
6721
|
+
for (let n = 2; existsSync5(to) && n < 100; n++) to = join7(legacyDir, `${name}-${n}`);
|
|
6547
6722
|
renameSync3(from, to);
|
|
6548
6723
|
moved++;
|
|
6549
6724
|
} catch {
|
|
@@ -6609,8 +6784,8 @@ function latestRunLaunch(runLogsDir, runLogKeys) {
|
|
|
6609
6784
|
}
|
|
6610
6785
|
if (indexes.size === 0) return null;
|
|
6611
6786
|
const index = Math.max(...indexes);
|
|
6612
|
-
const out = [
|
|
6613
|
-
const err =
|
|
6787
|
+
const out = [join8(dir, `${index}.json`), join8(dir, `${index}.json.done`)].find((p) => existsSync6(p)) ?? null;
|
|
6788
|
+
const err = existsSync6(join8(dir, `${index}.err.log`)) ? join8(dir, `${index}.err.log`) : null;
|
|
6614
6789
|
return { index, total: indexes.size, out, err };
|
|
6615
6790
|
}
|
|
6616
6791
|
function runLogTail(runLogsDir, runLogKeys, opts) {
|
|
@@ -6692,6 +6867,11 @@ async function serveLogRequests(deps, commands) {
|
|
|
6692
6867
|
init_src();
|
|
6693
6868
|
var RESOLVER_TERMINAL = /* @__PURE__ */ new Set([...TERMINAL_STATUSES, "stalled"]);
|
|
6694
6869
|
var SLUG_TERMINAL = new Set(TERMINAL_STATUSES);
|
|
6870
|
+
function toolErrorText(res) {
|
|
6871
|
+
const text = (res?.content ?? []).map((c) => c.text ?? "").join(" ").replace(/\s+/g, " ").trim();
|
|
6872
|
+
if (!text) return "\u043F\u0440\u043E\u0434 \u043E\u0442\u0432\u0435\u0440\u0433 \u0432\u044B\u0437\u043E\u0432 \u0431\u0435\u0437 \u043E\u0431\u044A\u044F\u0441\u043D\u0435\u043D\u0438\u044F";
|
|
6873
|
+
return text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
|
|
6874
|
+
}
|
|
6695
6875
|
function runRef(runKey) {
|
|
6696
6876
|
return { __runRef: runKey };
|
|
6697
6877
|
}
|
|
@@ -6777,6 +6957,17 @@ var SwarmClient = class _SwarmClient {
|
|
|
6777
6957
|
// нечего искать. Резолв делает прод (см. resolveDepRefs), здесь только запоминаем.
|
|
6778
6958
|
depSlug = /* @__PURE__ */ new Map();
|
|
6779
6959
|
static DEPS_TTL_MS = 6e4;
|
|
6960
|
+
// TASWM-486: ссылки, по которым прод НЕ ОТВЕТИЛ (отверг вызов или не дозвонились) —
|
|
6961
|
+
// с какого времени, сколько попыток и чем именно отказал. Отказ ≠ ответ: вердикта в
|
|
6962
|
+
// `depsProd` не появляется, `depVerdicts` отдаёт `pending`, и ждущий прогон стоит,
|
|
6963
|
+
// пока человек не разбудит его руками (21.08 — дважды за час). Отсюда две вещи:
|
|
6964
|
+
// короткий backoff вместо полного TTL (переспросить, а не ждать минуту) и
|
|
6965
|
+
// `depsUnconfirmed` — чтобы «стою вслепую» было ВИДНО в причине отказа и в аномалии.
|
|
6966
|
+
depsUnanswered = /* @__PURE__ */ new Map();
|
|
6967
|
+
// Первый переспрос — через 5 с (тик ~4 с, то есть буквально «на следующем тике»),
|
|
6968
|
+
// дальше удвоением до потолка TTL: устойчиво отвергающий прод (старее демона, скоуп
|
|
6969
|
+
// токена) не должен получать вопрос каждые четыре секунды до конца своей жизни.
|
|
6970
|
+
static DEPS_RETRY_BASE_MS = 5e3;
|
|
6780
6971
|
// TASWM-327: прод старее демона и не знает аргумент `level` у swarm_notify. Выясняется
|
|
6781
6972
|
// ровно один раз (первой отвергнутой нотификацией) и держится всю жизнь процесса — иначе
|
|
6782
6973
|
// каждый сигнал платил бы за это лишним отвергнутым вызовом. См. notifyAnomaly.
|
|
@@ -6835,26 +7026,26 @@ var SwarmClient = class _SwarmClient {
|
|
|
6835
7026
|
this.repoFullNames = registry.map((e) => e.repoFullName);
|
|
6836
7027
|
this.repoPathByFullName = new Map(registry.map((e) => [e.repoFullName, normalizeRepo(e.path)]));
|
|
6837
7028
|
this.host = opts.host ?? null;
|
|
6838
|
-
this.repoExists = opts.repoExists ??
|
|
7029
|
+
this.repoExists = opts.repoExists ?? existsSync7;
|
|
6839
7030
|
this.questionSyncMs = opts.questionSyncMs ?? 3e3;
|
|
6840
7031
|
this.executorId = opts.executorId ?? EXECUTOR.id;
|
|
6841
7032
|
this.outboxPath = opts.outboxPath;
|
|
6842
7033
|
this.mapPath = `${opts.outboxPath}.prodmap.json`;
|
|
6843
7034
|
this.stormPath = `${opts.outboxPath}.storm.jsonl`;
|
|
6844
|
-
|
|
7035
|
+
mkdirSync6(dirname2(this.outboxPath), { recursive: true });
|
|
6845
7036
|
this.recover();
|
|
6846
7037
|
}
|
|
6847
7038
|
// ── рестарт: поднять outbox (незаписанные операции) и карту runKey→prodId ─────
|
|
6848
7039
|
recover() {
|
|
6849
|
-
if (
|
|
7040
|
+
if (existsSync7(this.mapPath)) {
|
|
6850
7041
|
try {
|
|
6851
|
-
const obj = JSON.parse(
|
|
7042
|
+
const obj = JSON.parse(readFileSync5(this.mapPath, "utf8"));
|
|
6852
7043
|
for (const [k, v] of Object.entries(obj)) this.prodIdByKey.set(k, v);
|
|
6853
7044
|
} catch {
|
|
6854
7045
|
}
|
|
6855
7046
|
}
|
|
6856
|
-
if (
|
|
6857
|
-
const txt =
|
|
7047
|
+
if (existsSync7(this.outboxPath)) {
|
|
7048
|
+
const txt = readFileSync5(this.outboxPath, "utf8");
|
|
6858
7049
|
for (const line of txt.split("\n")) {
|
|
6859
7050
|
const s = line.trim();
|
|
6860
7051
|
if (!s) continue;
|
|
@@ -6874,10 +7065,10 @@ var SwarmClient = class _SwarmClient {
|
|
|
6874
7065
|
// eventsLog ПЕРВЫМИ (до hydrate), в порядке файла (= хронологический); id
|
|
6875
7066
|
// переприсваиваем локально (id событий — только порядок, не корреляция с продом).
|
|
6876
7067
|
replayGlobalEvents() {
|
|
6877
|
-
if (!
|
|
7068
|
+
if (!existsSync7(this.stormPath)) return;
|
|
6878
7069
|
let txt;
|
|
6879
7070
|
try {
|
|
6880
|
-
txt =
|
|
7071
|
+
txt = readFileSync5(this.stormPath, "utf8");
|
|
6881
7072
|
} catch {
|
|
6882
7073
|
return;
|
|
6883
7074
|
}
|
|
@@ -6963,9 +7154,11 @@ var SwarmClient = class _SwarmClient {
|
|
|
6963
7154
|
if ("duration_ms" in patch && patch.duration_ms !== void 0) out.durationMs = patch.duration_ms;
|
|
6964
7155
|
if ("respawns" in patch && patch.respawns !== void 0) out.respawns = patch.respawns;
|
|
6965
7156
|
if ("pr_url" in patch) out.prUrl = patch.pr_url ?? null;
|
|
7157
|
+
if ("changed_paths" in patch && patch.changed_paths) out.changedPaths = patch.changed_paths;
|
|
6966
7158
|
if ("branch" in patch) out.branch = patch.branch ?? null;
|
|
6967
7159
|
if ("repo" in patch) out.repo = patch.repo ?? null;
|
|
6968
7160
|
if ("stop_reason" in patch) out.stopReason = patch.stop_reason ?? null;
|
|
7161
|
+
if ("return_reason" in patch) out.returnReason = patch.return_reason ?? null;
|
|
6969
7162
|
if ("depends_on" in patch && patch.depends_on) out.dependsOn = patch.depends_on;
|
|
6970
7163
|
if ("decision" in patch) out.decision = patch.decision ? JSON.stringify(patch.decision) : null;
|
|
6971
7164
|
if ("kind" in patch && patch.kind !== void 0) out.role = patch.kind;
|
|
@@ -7041,9 +7234,12 @@ var SwarmClient = class _SwarmClient {
|
|
|
7041
7234
|
tokens_out: 0,
|
|
7042
7235
|
duration_ms: 0,
|
|
7043
7236
|
pr_url: null,
|
|
7237
|
+
changed_paths: [],
|
|
7238
|
+
// файлы прогона снимаются при открытии PR (TASWM-506)
|
|
7044
7239
|
superseded_by: null,
|
|
7045
7240
|
decision: input.decision ?? null,
|
|
7046
7241
|
stop_reason: null,
|
|
7242
|
+
return_reason: null,
|
|
7047
7243
|
respawns: 0,
|
|
7048
7244
|
resolves_run_id: input.resolves_run_id ?? null,
|
|
7049
7245
|
reviews_run_id: input.reviews_run_id ?? null,
|
|
@@ -7354,27 +7550,41 @@ var SwarmClient = class _SwarmClient {
|
|
|
7354
7550
|
* Одним вызовом на все просроченные ссылки, а не по вызову на ссылку: очередь ждущих
|
|
7355
7551
|
* бывает в десятки прогонов, и N запросов на тик — это тот же самый поход в прод, только
|
|
7356
7552
|
* дороже.
|
|
7553
|
+
*
|
|
7554
|
+
* TASWM-486: ОТКАЗ ПРОДА — НЕ ОТВЕТ. Раньше `isError` засчитывался как «спросили», и
|
|
7555
|
+
* следующая попытка шла только через TTL; при УСТОЙЧИВОМ отказе (прод старее демона,
|
|
7556
|
+
* просроченный скоуп токена демона, сбой на батче) вердикта не появлялось никогда —
|
|
7557
|
+
* зависимость навсегда оставалась `pending`, а ждущий прогон стоял при свободной машине
|
|
7558
|
+
* до ручного `wake_run`. Теперь отказ помнится отдельно (`depsUnanswered`): переспрос идёт
|
|
7559
|
+
* по короткому нарастающему backoff, а сам факт «прод молчит» становится видимым —
|
|
7560
|
+
* причина отказа спавна называет его вслух, и на затянувшуюся слепоту звенит аномалия.
|
|
7357
7561
|
*/
|
|
7358
7562
|
async refreshDeps(refs, nowMs = Date.now()) {
|
|
7359
7563
|
const ask = [...new Set(refs)].filter((ref) => {
|
|
7360
7564
|
if (!ref || this.depsProd.get(ref)?.outcome === "done") return false;
|
|
7361
7565
|
const asked = this.depsAskedAt.get(ref);
|
|
7362
|
-
|
|
7566
|
+
if (asked === void 0) return true;
|
|
7567
|
+
return nowMs - asked >= this.depsRetryDelay(ref);
|
|
7363
7568
|
});
|
|
7364
7569
|
if (!ask.length) return;
|
|
7365
7570
|
for (const ref of ask) this.depsAskedAt.set(ref, nowMs);
|
|
7366
7571
|
let res;
|
|
7367
7572
|
try {
|
|
7368
7573
|
res = await this.call("swarm_deps_status", { refs: ask });
|
|
7369
|
-
} catch {
|
|
7574
|
+
} catch (e) {
|
|
7370
7575
|
for (const ref of ask) this.depsAskedAt.delete(ref);
|
|
7576
|
+
this.noteDepsUnanswered(ask, nowMs, String(e));
|
|
7577
|
+
return;
|
|
7578
|
+
}
|
|
7579
|
+
if (res?.isError) {
|
|
7580
|
+
this.noteDepsUnanswered(ask, nowMs, toolErrorText(res));
|
|
7371
7581
|
return;
|
|
7372
7582
|
}
|
|
7373
|
-
if (res?.isError) return;
|
|
7374
7583
|
const deps = res.structuredContent?.deps ?? [];
|
|
7375
7584
|
for (const d of deps) {
|
|
7376
7585
|
const ref = typeof d.ref === "string" ? d.ref : null;
|
|
7377
7586
|
if (!ref) continue;
|
|
7587
|
+
this.depsUnanswered.delete(ref);
|
|
7378
7588
|
const outcome = String(d.outcome ?? "pending");
|
|
7379
7589
|
this.depsProd.set(ref, {
|
|
7380
7590
|
outcome,
|
|
@@ -7386,6 +7596,75 @@ var SwarmClient = class _SwarmClient {
|
|
|
7386
7596
|
if (typeof d.slug === "string" && d.slug) this.depSlug.set(ref, d.slug);
|
|
7387
7597
|
}
|
|
7388
7598
|
}
|
|
7599
|
+
/**
|
|
7600
|
+
* Через сколько переспрашивать ссылку (TASWM-486). Пауза зависит от того, ЧЕГО мы ждём —
|
|
7601
|
+
* и сколько стоит опоздать с ответом:
|
|
7602
|
+
*
|
|
7603
|
+
* • ПРОД ОТКАЗАЛ (isError / не дозвонились) — короткий backoff с удвоением (5с →
|
|
7604
|
+
* потолок TTL). Ждём, что прод вообще начнёт отвечать; до тех пор вердикта нет вовсе.
|
|
7605
|
+
* • БЛОКЕР ЖИВ либо вердикта ещё не было (`pending`) — КАЖДЫЙ ТИК. Это единственное
|
|
7606
|
+
* состояние, в котором ждущий прогон ЖЖЁТ ВРЕМЯ: блокер может закрыться в любую
|
|
7607
|
+
* секунду, и до TASWM-486 минута TTL превращалась в минуту простоя при свободной
|
|
7608
|
+
* машине (21.08: «стартует сам в пределах одного-двух тиков» не выполнялось даже
|
|
7609
|
+
* когда прод отвечал исправно). Цена — ОДИН батч-вызов на тик (все ссылки очереди
|
|
7610
|
+
* идут одним запросом, см. refreshDeps), и только пока кто-то реально ждёт.
|
|
7611
|
+
* • ЗАВИСИМОСТЬ УЖЕ СНЯТА неуспехом (`exhausted`) или блокера не знает никто
|
|
7612
|
+
* (`unknown`) — штатный TTL: прогон этими вердиктами больше не задерживается
|
|
7613
|
+
* (`exhausted`) либо не поедет в любом случае (`unknown`, там своя аномалия), и
|
|
7614
|
+
* переспрос нужен только на случай, что оператор переиграет блокер новым прогоном.
|
|
7615
|
+
* • `done` сюда не доходит вовсе — монотонен, отфильтрован раньше.
|
|
7616
|
+
*/
|
|
7617
|
+
depsRetryDelay(ref) {
|
|
7618
|
+
const fail = this.depsUnanswered.get(ref);
|
|
7619
|
+
if (fail) {
|
|
7620
|
+
const backoff = _SwarmClient.DEPS_RETRY_BASE_MS * 2 ** Math.min(fail.attempts - 1, 10);
|
|
7621
|
+
return Math.min(backoff, _SwarmClient.DEPS_TTL_MS);
|
|
7622
|
+
}
|
|
7623
|
+
const known = this.depsProd.get(ref);
|
|
7624
|
+
if (!known || known.outcome === "pending") return 0;
|
|
7625
|
+
return _SwarmClient.DEPS_TTL_MS;
|
|
7626
|
+
}
|
|
7627
|
+
/** Запомнить, что прод по этим ссылкам не ответил (см. depsUnanswered / TASWM-486). */
|
|
7628
|
+
noteDepsUnanswered(refs, nowMs, error) {
|
|
7629
|
+
for (const ref of refs) {
|
|
7630
|
+
const prev = this.depsUnanswered.get(ref);
|
|
7631
|
+
this.depsUnanswered.set(ref, {
|
|
7632
|
+
ref,
|
|
7633
|
+
slug: this.depSlug.get(ref) ?? ref,
|
|
7634
|
+
// `since` — начало ЭПИЗОДА слепоты, а не последняя попытка: именно возраст эпизода
|
|
7635
|
+
// отвечает на вопрос «сколько прогон уже стоит вслепую».
|
|
7636
|
+
since: prev?.since ?? nowMs,
|
|
7637
|
+
attempts: (prev?.attempts ?? 0) + 1,
|
|
7638
|
+
error
|
|
7639
|
+
});
|
|
7640
|
+
}
|
|
7641
|
+
}
|
|
7642
|
+
/**
|
|
7643
|
+
* TASWM-486: ссылки `dependsOn`, по которым ВЕРДИКТА ПРОДА НЕТ — не «блокер жив», а «мы
|
|
7644
|
+
* не знаем, жив ли он». Снаружи оба выглядели одинаково (`pending`) и вели себя одинаково
|
|
7645
|
+
* молча, а разница ровно в том, дождётся ли ждущий чего-нибудь сам: живой блокер
|
|
7646
|
+
* закончится, а неотвечающий прод — нет, и прогон стоит до ручной побудки.
|
|
7647
|
+
*
|
|
7648
|
+
* Что сюда НЕ попадает и почему:
|
|
7649
|
+
* • прод отвечает — слепоты нет вовсе (запись стирается первым же ответом);
|
|
7650
|
+
* • последний вердикт уже СНЯЛ зависимость (`met`) — прогон ею не задерживается,
|
|
7651
|
+
* свежесть ответа ничего не решает;
|
|
7652
|
+
* • последний вердикт — `unknown`: это территория `dep_missing`, у неё и текст
|
|
7653
|
+
* точнее («блокера не знает никто»), и лечение другое.
|
|
7654
|
+
* А вот УСТАРЕВШИЙ `pending` при замолчавшем проде — как раз слепота: когда-то блокер
|
|
7655
|
+
* был жив, с тех пор он мог закрыться, и узнать это не у кого.
|
|
7656
|
+
*/
|
|
7657
|
+
depsUnconfirmed(refs) {
|
|
7658
|
+
const out = [];
|
|
7659
|
+
for (const ref of new Set(refs)) {
|
|
7660
|
+
const known = this.depsProd.get(ref);
|
|
7661
|
+
if (known?.met || known?.outcome === "unknown") continue;
|
|
7662
|
+
const fail = this.depsUnanswered.get(ref);
|
|
7663
|
+
if (!fail) continue;
|
|
7664
|
+
out.push({ ...fail, slug: this.depSlug.get(ref) ?? fail.slug });
|
|
7665
|
+
}
|
|
7666
|
+
return out;
|
|
7667
|
+
}
|
|
7389
7668
|
/**
|
|
7390
7669
|
* TASWM-300: точечно перечитать прогон из прода — см. докблок SwarmStore.refreshRun.
|
|
7391
7670
|
* `swarm_list_runs` фильтруем по задаче прогона (taskId принимает человеко-слаг) и берём
|
|
@@ -7702,7 +7981,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
7702
7981
|
* транзиентным сбоем ОС, который этот PR должен переживать, а не превращать в мор.
|
|
7703
7982
|
*/
|
|
7704
7983
|
reconcileRuntime(opts) {
|
|
7705
|
-
const wtExists = opts.worktreeExists ??
|
|
7984
|
+
const wtExists = opts.worktreeExists ?? existsSync7;
|
|
7706
7985
|
const pidByWorktree = /* @__PURE__ */ new Map();
|
|
7707
7986
|
try {
|
|
7708
7987
|
for (const p of opts.listProcesses()) if (p.worktree) pidByWorktree.set(p.worktree, p.pid);
|
|
@@ -8562,9 +8841,12 @@ var SwarmClient = class _SwarmClient {
|
|
|
8562
8841
|
tokens_out: 0,
|
|
8563
8842
|
duration_ms: 0,
|
|
8564
8843
|
pr_url: null,
|
|
8844
|
+
changed_paths: [],
|
|
8845
|
+
// занятые файлы (TASWM-506) — прод-поле, заполняется ниже
|
|
8565
8846
|
superseded_by: null,
|
|
8566
8847
|
decision: null,
|
|
8567
8848
|
stop_reason: null,
|
|
8849
|
+
return_reason: null,
|
|
8568
8850
|
respawns: 0,
|
|
8569
8851
|
resolves_run_id: null,
|
|
8570
8852
|
reviews_run_id: null,
|
|
@@ -8620,6 +8902,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
8620
8902
|
const verProd = pr.verifiesRunId ?? null;
|
|
8621
8903
|
if (verProd) local.verifies_run_id = this.localIdForProdId(verProd) ?? local.verifies_run_id;
|
|
8622
8904
|
local.verify_verdict = toVerifyVerdict(pr.verifyVerdict) ?? local.verify_verdict;
|
|
8905
|
+
local.return_reason = toReturnReason(pr.returnReason) ?? local.return_reason;
|
|
8623
8906
|
local.review_phase = pr.reviewPhase ?? local.review_phase;
|
|
8624
8907
|
local.repo = this.resolveRepo(pr.repo ?? null);
|
|
8625
8908
|
local.host_id = pr.hostId ?? null;
|
|
@@ -8639,6 +8922,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
8639
8922
|
local.tokens_out = typeof pr.tokensOut === "number" ? pr.tokensOut : 0;
|
|
8640
8923
|
local.duration_ms = typeof pr.durationMs === "number" ? pr.durationMs : 0;
|
|
8641
8924
|
local.pr_url = pr.prUrl ?? null;
|
|
8925
|
+
local.changed_paths = Array.isArray(pr.changedPaths) ? pr.changedPaths : local.changed_paths ?? [];
|
|
8642
8926
|
local.stop_reason = pr.stopReason ?? null;
|
|
8643
8927
|
local.respawns = typeof pr.respawns === "number" ? pr.respawns : 0;
|
|
8644
8928
|
local.decision = parseDecision(pr.decision);
|
|
@@ -8895,13 +9179,13 @@ var SwarmClient = class _SwarmClient {
|
|
|
8895
9179
|
}
|
|
8896
9180
|
persistPending() {
|
|
8897
9181
|
try {
|
|
8898
|
-
|
|
9182
|
+
writeFileSync5(this.outboxPath, this.pending.map((e) => JSON.stringify(e)).join("\n") + (this.pending.length ? "\n" : ""));
|
|
8899
9183
|
} catch {
|
|
8900
9184
|
}
|
|
8901
9185
|
}
|
|
8902
9186
|
persistMap() {
|
|
8903
9187
|
try {
|
|
8904
|
-
|
|
9188
|
+
writeFileSync5(this.mapPath, JSON.stringify(Object.fromEntries(this.prodIdByKey)));
|
|
8905
9189
|
} catch {
|
|
8906
9190
|
}
|
|
8907
9191
|
}
|
|
@@ -8971,7 +9255,7 @@ function tickPulse(health, now, intervalMs) {
|
|
|
8971
9255
|
}
|
|
8972
9256
|
|
|
8973
9257
|
// src/logRotate.ts
|
|
8974
|
-
import { copyFileSync, renameSync as renameSync5, statSync as statSync5, fstatSync, truncateSync, unlinkSync as unlinkSync2, existsSync as
|
|
9258
|
+
import { copyFileSync as copyFileSync2, renameSync as renameSync5, statSync as statSync5, fstatSync, truncateSync, unlinkSync as unlinkSync2, existsSync as existsSync8 } from "fs";
|
|
8975
9259
|
var realIo = {
|
|
8976
9260
|
size: (path) => {
|
|
8977
9261
|
try {
|
|
@@ -8980,11 +9264,11 @@ var realIo = {
|
|
|
8980
9264
|
return null;
|
|
8981
9265
|
}
|
|
8982
9266
|
},
|
|
8983
|
-
copy:
|
|
9267
|
+
copy: copyFileSync2,
|
|
8984
9268
|
rename: renameSync5,
|
|
8985
9269
|
truncate: (path) => truncateSync(path, 0),
|
|
8986
9270
|
unlink: unlinkSync2,
|
|
8987
|
-
exists:
|
|
9271
|
+
exists: existsSync8
|
|
8988
9272
|
};
|
|
8989
9273
|
var realOwnerIo = {
|
|
8990
9274
|
statFd: (fd) => {
|
|
@@ -9111,13 +9395,13 @@ async function buildRepoRegistry(paths) {
|
|
|
9111
9395
|
}
|
|
9112
9396
|
|
|
9113
9397
|
// src/repoMemory.ts
|
|
9114
|
-
import { readFileSync as
|
|
9115
|
-
import { join as
|
|
9398
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
9399
|
+
import { join as join10 } from "path";
|
|
9116
9400
|
var FILE = "repos.json";
|
|
9117
9401
|
function loadRememberedRepos(home) {
|
|
9118
9402
|
let raw;
|
|
9119
9403
|
try {
|
|
9120
|
-
raw =
|
|
9404
|
+
raw = readFileSync8(join10(home, FILE), "utf8");
|
|
9121
9405
|
} catch {
|
|
9122
9406
|
return [];
|
|
9123
9407
|
}
|
|
@@ -9132,7 +9416,7 @@ function loadRememberedRepos(home) {
|
|
|
9132
9416
|
function rememberRepos(home, paths, now = () => /* @__PURE__ */ new Date()) {
|
|
9133
9417
|
const clean = paths.filter((p) => p.trim().length > 0);
|
|
9134
9418
|
if (clean.length === 0) return;
|
|
9135
|
-
|
|
9419
|
+
writeFileSync8(join10(home, FILE), `${JSON.stringify({ paths: clean, at: now().toISOString() }, null, 2)}
|
|
9136
9420
|
`);
|
|
9137
9421
|
}
|
|
9138
9422
|
function pickRepoPaths(args) {
|
|
@@ -9146,13 +9430,13 @@ function pickRepoPaths(args) {
|
|
|
9146
9430
|
init_src();
|
|
9147
9431
|
init_config();
|
|
9148
9432
|
init_linkFailure();
|
|
9149
|
-
import { homedir as
|
|
9150
|
-
import { readFileSync as
|
|
9151
|
-
import { isAbsolute, join as
|
|
9433
|
+
import { homedir as homedir4 } from "os";
|
|
9434
|
+
import { readFileSync as readFileSync10, renameSync as renameSync6, writeFileSync as writeFileSync9 } from "fs";
|
|
9435
|
+
import { isAbsolute, join as join12, resolve as resolve2 } from "path";
|
|
9152
9436
|
|
|
9153
9437
|
// src/repoScan.ts
|
|
9154
|
-
import { readdirSync as readdirSync6, readFileSync as
|
|
9155
|
-
import { join as
|
|
9438
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync6 } from "fs";
|
|
9439
|
+
import { join as join11 } from "path";
|
|
9156
9440
|
var SKIP = /* @__PURE__ */ new Set([
|
|
9157
9441
|
"node_modules",
|
|
9158
9442
|
"vendor",
|
|
@@ -9182,7 +9466,7 @@ function parseOriginFromGitConfig(text) {
|
|
|
9182
9466
|
}
|
|
9183
9467
|
function originOf(dir) {
|
|
9184
9468
|
try {
|
|
9185
|
-
return parseOriginFromGitConfig(
|
|
9469
|
+
return parseOriginFromGitConfig(readFileSync9(join11(dir, ".git", "config"), "utf8"));
|
|
9186
9470
|
} catch {
|
|
9187
9471
|
return null;
|
|
9188
9472
|
}
|
|
@@ -9217,7 +9501,7 @@ function walk(dir, depth, maxDepth, found) {
|
|
|
9217
9501
|
}
|
|
9218
9502
|
for (const name of entries) {
|
|
9219
9503
|
if (name.startsWith(".") || SKIP.has(name)) continue;
|
|
9220
|
-
const child =
|
|
9504
|
+
const child = join11(dir, name);
|
|
9221
9505
|
try {
|
|
9222
9506
|
if (!statSync6(child).isDirectory()) continue;
|
|
9223
9507
|
} catch {
|
|
@@ -9239,13 +9523,13 @@ function defaultScanRoots(home, knownPaths = []) {
|
|
|
9239
9523
|
if (parent && parent !== p) add(parent);
|
|
9240
9524
|
}
|
|
9241
9525
|
for (const name of ["projects", "code", "src", "dev", "work", "repos", "git", "Projects", "Code", "Developer", "Documents/GitHub", "go/src"]) {
|
|
9242
|
-
add(
|
|
9526
|
+
add(join11(home, name));
|
|
9243
9527
|
}
|
|
9244
9528
|
return out;
|
|
9245
9529
|
}
|
|
9246
9530
|
|
|
9247
9531
|
// src/repoDiscovery.ts
|
|
9248
|
-
function resolveScanRoots(raw, home =
|
|
9532
|
+
function resolveScanRoots(raw, home = homedir4(), knownPaths = []) {
|
|
9249
9533
|
const v = (raw ?? "").trim();
|
|
9250
9534
|
if (!v) return defaultScanRoots(home, knownPaths);
|
|
9251
9535
|
if (["off", "none", "0"].includes(v.toLowerCase())) return [];
|
|
@@ -9253,7 +9537,7 @@ function resolveScanRoots(raw, home = homedir3(), knownPaths = []) {
|
|
|
9253
9537
|
for (const part of v.split(",")) {
|
|
9254
9538
|
const p = part.trim();
|
|
9255
9539
|
if (!p) continue;
|
|
9256
|
-
const abs = p === "~" ? home : p.startsWith("~/") ?
|
|
9540
|
+
const abs = p === "~" ? home : p.startsWith("~/") ? join12(home, p.slice(2)) : isAbsolute(p) ? p : resolve2(home, p);
|
|
9257
9541
|
if (!out.includes(abs)) out.push(abs);
|
|
9258
9542
|
}
|
|
9259
9543
|
return out;
|
|
@@ -9467,7 +9751,7 @@ function repoMapRows(state) {
|
|
|
9467
9751
|
}
|
|
9468
9752
|
function loadRepoMapCache(path) {
|
|
9469
9753
|
try {
|
|
9470
|
-
const raw = JSON.parse(
|
|
9754
|
+
const raw = JSON.parse(readFileSync10(path, "utf8"));
|
|
9471
9755
|
if (!Array.isArray(raw?.entries)) return { scannedAt: null, entries: [] };
|
|
9472
9756
|
return {
|
|
9473
9757
|
scannedAt: typeof raw.scannedAt === "string" && raw.scannedAt ? raw.scannedAt : null,
|
|
@@ -9482,7 +9766,7 @@ function loadRepoMapCache(path) {
|
|
|
9482
9766
|
function writeRepoMapCache(path, entries, scannedAt = stamp()) {
|
|
9483
9767
|
try {
|
|
9484
9768
|
const tmp = `${path}.tmp`;
|
|
9485
|
-
|
|
9769
|
+
writeFileSync9(tmp, JSON.stringify({ at: stamp(), scannedAt, entries }, null, 2));
|
|
9486
9770
|
renameSync6(tmp, path);
|
|
9487
9771
|
} catch {
|
|
9488
9772
|
}
|
|
@@ -9490,17 +9774,17 @@ function writeRepoMapCache(path, entries, scannedAt = stamp()) {
|
|
|
9490
9774
|
|
|
9491
9775
|
// src/hostIdentity.ts
|
|
9492
9776
|
import { hostname } from "os";
|
|
9493
|
-
import { existsSync as
|
|
9494
|
-
import { join as
|
|
9777
|
+
import { existsSync as existsSync9, readFileSync as readFileSync11, renameSync as renameSync7, writeFileSync as writeFileSync10 } from "fs";
|
|
9778
|
+
import { join as join13 } from "path";
|
|
9495
9779
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
9496
9780
|
function hostIdentityPath(home) {
|
|
9497
|
-
return
|
|
9781
|
+
return join13(home, "host.json");
|
|
9498
9782
|
}
|
|
9499
9783
|
function loadHostIdentity(deps) {
|
|
9500
|
-
const exists = deps.exists ??
|
|
9501
|
-
const readFile2 = deps.readFile ?? ((p) =>
|
|
9784
|
+
const exists = deps.exists ?? existsSync9;
|
|
9785
|
+
const readFile2 = deps.readFile ?? ((p) => readFileSync11(p, "utf8"));
|
|
9502
9786
|
const writeFile3 = deps.writeFile ?? ((p, data) => {
|
|
9503
|
-
|
|
9787
|
+
writeFileSync10(`${p}.tmp`, data);
|
|
9504
9788
|
renameSync7(`${p}.tmp`, p);
|
|
9505
9789
|
});
|
|
9506
9790
|
const host = deps.hostname ?? hostname;
|
|
@@ -9683,15 +9967,15 @@ function buildReliefCostCandidates(cost, t, sustainMs, build) {
|
|
|
9683
9967
|
|
|
9684
9968
|
// src/launcher.ts
|
|
9685
9969
|
import { spawn } from "child_process";
|
|
9686
|
-
import { closeSync as closeSync2, existsSync as
|
|
9970
|
+
import { closeSync as closeSync2, existsSync as existsSync10, mkdirSync as mkdirSync8, openSync as openSync2, readdirSync as readdirSync8 } from "fs";
|
|
9687
9971
|
import { createRequire } from "module";
|
|
9688
|
-
import { homedir as
|
|
9689
|
-
import { dirname as
|
|
9690
|
-
import { fileURLToPath } from "url";
|
|
9972
|
+
import { homedir as homedir5 } from "os";
|
|
9973
|
+
import { dirname as dirname3, join as join15 } from "path";
|
|
9974
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9691
9975
|
|
|
9692
9976
|
// src/droneHome.ts
|
|
9693
|
-
import { lstatSync, mkdirSync as
|
|
9694
|
-
import { join as
|
|
9977
|
+
import { lstatSync, mkdirSync as mkdirSync7, readdirSync as readdirSync7, rmSync, statSync as statSync7, symlinkSync as symlinkSync2 } from "fs";
|
|
9978
|
+
import { join as join14, relative, sep } from "path";
|
|
9695
9979
|
var DRONE_HOME_DENY_DEFAULT = [".taskless-swarm", ".bash_history", ".zsh_history"];
|
|
9696
9980
|
function droneHomeDeny(env = process.env) {
|
|
9697
9981
|
const extra = (env.SWARM_DRONE_HOME_DENY || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -9709,26 +9993,26 @@ function buildDroneHome(opts) {
|
|
|
9709
9993
|
const { realHome, droneHome } = opts;
|
|
9710
9994
|
const deny = opts.deny ?? droneHomeDeny();
|
|
9711
9995
|
const platform = opts.platform ?? process.platform;
|
|
9712
|
-
|
|
9996
|
+
mkdirSync7(droneHome, { recursive: true, mode: 448 });
|
|
9713
9997
|
const wanted = new Set(mirroredEntries(readdirSync7(realHome), deny, platform));
|
|
9714
9998
|
const rel = relative(realHome, droneHome);
|
|
9715
9999
|
if (rel && !rel.startsWith("..") && !rel.includes(sep)) wanted.delete(rel);
|
|
9716
10000
|
for (const existing of readdirSync7(droneHome)) {
|
|
9717
10001
|
if (wanted.has(existing)) continue;
|
|
9718
|
-
const p =
|
|
10002
|
+
const p = join14(droneHome, existing);
|
|
9719
10003
|
try {
|
|
9720
10004
|
if (lstatSync(p).isSymbolicLink()) rmSync(p);
|
|
9721
10005
|
} catch {
|
|
9722
10006
|
}
|
|
9723
10007
|
}
|
|
9724
10008
|
for (const name of wanted) {
|
|
9725
|
-
const link =
|
|
10009
|
+
const link = join14(droneHome, name);
|
|
9726
10010
|
try {
|
|
9727
10011
|
lstatSync(link);
|
|
9728
10012
|
continue;
|
|
9729
10013
|
} catch {
|
|
9730
10014
|
}
|
|
9731
|
-
const target =
|
|
10015
|
+
const target = join14(realHome, name);
|
|
9732
10016
|
let isDir;
|
|
9733
10017
|
try {
|
|
9734
10018
|
isDir = statSync7(target).isDirectory();
|
|
@@ -9749,7 +10033,7 @@ var REAL_PATH_ENV_PINS = [
|
|
|
9749
10033
|
{ env: "YARN_GLOBAL_FOLDER", segments: [".yarn", "berry"] }
|
|
9750
10034
|
];
|
|
9751
10035
|
function realPathEnvPins(realHome) {
|
|
9752
|
-
return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env,
|
|
10036
|
+
return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env, join14(realHome, ...segments)]));
|
|
9753
10037
|
}
|
|
9754
10038
|
function dronePrivatePaths(realHome, swarmHome) {
|
|
9755
10039
|
return [
|
|
@@ -9805,7 +10089,7 @@ function droneTestEnv(droneCap, base3 = process.env, host = hostForkSpec()) {
|
|
|
9805
10089
|
|
|
9806
10090
|
// src/swarmModels.ts
|
|
9807
10091
|
init_src();
|
|
9808
|
-
function
|
|
10092
|
+
function pick2(v) {
|
|
9809
10093
|
const s = typeof v === "string" ? v.trim() : "";
|
|
9810
10094
|
return s.length > 0 ? s : null;
|
|
9811
10095
|
}
|
|
@@ -9821,10 +10105,10 @@ function forKind(kind, swarm, norm) {
|
|
|
9821
10105
|
}
|
|
9822
10106
|
function resolveRunModelRef(run, swarm, env = process.env) {
|
|
9823
10107
|
const configured = parseModelRef(
|
|
9824
|
-
|
|
10108
|
+
pick2(run.model) ?? forKind(run.kind, swarm, pick2) ?? pick2(env.SWARM_DRONE_MODEL)
|
|
9825
10109
|
);
|
|
9826
10110
|
if (configured) return configured;
|
|
9827
|
-
const providerId =
|
|
10111
|
+
const providerId = pick2(env.SWARM_MODEL_PROVIDER) ?? DEFAULT_PROVIDER_ID;
|
|
9828
10112
|
return { providerId, model: defaultModelFor(modelRoleOf(run.kind), providerId) };
|
|
9829
10113
|
}
|
|
9830
10114
|
function resolveRunModel(run, swarm, env = process.env) {
|
|
@@ -9861,7 +10145,7 @@ function droneEnv(worktree, base3 = process.env, isolation, droneCap = null) {
|
|
|
9861
10145
|
const env = { ...base3 };
|
|
9862
10146
|
const isolate = base3.SWARM_TURBO_ISOLATE !== "0";
|
|
9863
10147
|
if (isolate && !base3.TURBO_CACHE_DIR) {
|
|
9864
|
-
env.TURBO_CACHE_DIR =
|
|
10148
|
+
env.TURBO_CACHE_DIR = join15(worktree, ".turbo", "cache");
|
|
9865
10149
|
}
|
|
9866
10150
|
Object.assign(env, droneTestEnv(droneCap, base3));
|
|
9867
10151
|
for (const key of DAEMON_STATE_ENV) delete env[key];
|
|
@@ -9889,18 +10173,18 @@ var DAEMON_STATE_ENV = [
|
|
|
9889
10173
|
"TASKLESS_TOKEN"
|
|
9890
10174
|
];
|
|
9891
10175
|
function bundledRoleDir(kind) {
|
|
9892
|
-
return
|
|
10176
|
+
return join15(dirname3(fileURLToPath2(import.meta.url)), "..", "roles", kind);
|
|
9893
10177
|
}
|
|
9894
10178
|
function roleDir(worktree, kind) {
|
|
9895
|
-
return
|
|
10179
|
+
return join15(worktree, ".claude", "roles", kind);
|
|
9896
10180
|
}
|
|
9897
10181
|
function roleDirs(worktree, kind) {
|
|
9898
|
-
return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) =>
|
|
10182
|
+
return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) => existsSync10(dir));
|
|
9899
10183
|
}
|
|
9900
10184
|
function nextRunLogPath(runLogsDir, runLogKeys) {
|
|
9901
10185
|
const dir = runLogDirForWrite(runLogsDir, runLogKeys);
|
|
9902
10186
|
if (!dir) throw new Error("\u043D\u0435\u0447\u0435\u043C \u0430\u0434\u0440\u0435\u0441\u043E\u0432\u0430\u0442\u044C \u043A\u0430\u0442\u0430\u043B\u043E\u0433 run-log'\u043E\u0432: \u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435\u0442 \u0441\u0442\u0430\u0431\u0438\u043B\u044C\u043D\u043E\u0433\u043E \u043A\u043B\u044E\u0447\u0430 (SwarmStore.runLogKeys)");
|
|
9903
|
-
|
|
10187
|
+
mkdirSync8(dir, { recursive: true });
|
|
9904
10188
|
let files = [];
|
|
9905
10189
|
try {
|
|
9906
10190
|
files = readdirSync8(dir);
|
|
@@ -9911,7 +10195,7 @@ function nextRunLogPath(runLogsDir, runLogKeys) {
|
|
|
9911
10195
|
const m = /^(\d+)\.json/.exec(f);
|
|
9912
10196
|
if (m) next = Math.max(next, Number(m[1]) + 1);
|
|
9913
10197
|
}
|
|
9914
|
-
return
|
|
10198
|
+
return join15(dir, `${next}.json`);
|
|
9915
10199
|
}
|
|
9916
10200
|
function openNextRunLog(runLogsDir, runLogKeys) {
|
|
9917
10201
|
for (let attempt = 0; attempt < 50; attempt++) {
|
|
@@ -9992,11 +10276,11 @@ function makeLauncher(cfg) {
|
|
|
9992
10276
|
} catch (e) {
|
|
9993
10277
|
console.error(`[launcher] \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0443\u0441\u0438\u043B\u0438\u0435 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${run.id}: ${String(e)}`);
|
|
9994
10278
|
}
|
|
9995
|
-
const realHome = cfg.droneIsolation?.realHome ??
|
|
10279
|
+
const realHome = cfg.droneIsolation?.realHome ?? homedir5();
|
|
9996
10280
|
const home = cfg.droneIsolation ? buildDroneHome({ realHome, droneHome: cfg.droneIsolation.home }) : void 0;
|
|
9997
|
-
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !
|
|
10281
|
+
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync10(join15(cfg.droneIsolation.swarmHome, "token"))) {
|
|
9998
10282
|
console.error(
|
|
9999
|
-
`[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${
|
|
10283
|
+
`[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${join15(cfg.droneIsolation.swarmHome, "token")} \u043D\u0435\u0442. read_task \u0434\u0440\u043E\u043D\u0430 \u0431\u0443\u0434\u0435\u0442 slug-only. \u041F\u043E\u0447\u0438\u043D\u0438: \`swarm login\` (oauth.json) \u0438\u043B\u0438 \u043F\u043E\u043B\u043E\u0436\u0438 \u0442\u043E\u043A\u0435\u043D \u0432 \u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B.`
|
|
10000
10284
|
);
|
|
10001
10285
|
}
|
|
10002
10286
|
const executor = cfg.executor ?? resolveExecutor();
|
|
@@ -10057,8 +10341,8 @@ function makeLauncher(cfg) {
|
|
|
10057
10341
|
init_procCleanup();
|
|
10058
10342
|
|
|
10059
10343
|
// src/cost.ts
|
|
10060
|
-
import { readFileSync as
|
|
10061
|
-
import { join as
|
|
10344
|
+
import { readFileSync as readFileSync12, readdirSync as readdirSync9, renameSync as renameSync8 } from "fs";
|
|
10345
|
+
import { join as join16 } from "path";
|
|
10062
10346
|
init_executors();
|
|
10063
10347
|
var UNPROCESSED_LOG_RE = /^\d+\.json$/;
|
|
10064
10348
|
function unprocessedLogFiles(dir) {
|
|
@@ -10080,10 +10364,10 @@ function accountRunCost(deps, run) {
|
|
|
10080
10364
|
let durationMs = 0;
|
|
10081
10365
|
const processed = [];
|
|
10082
10366
|
for (const f of files) {
|
|
10083
|
-
const full =
|
|
10367
|
+
const full = join16(dir, f);
|
|
10084
10368
|
let raw = null;
|
|
10085
10369
|
try {
|
|
10086
|
-
raw =
|
|
10370
|
+
raw = readFileSync12(full, "utf8");
|
|
10087
10371
|
} catch {
|
|
10088
10372
|
continue;
|
|
10089
10373
|
}
|
|
@@ -10132,8 +10416,8 @@ function makeCostSweeper(deps) {
|
|
|
10132
10416
|
}
|
|
10133
10417
|
|
|
10134
10418
|
// src/runOutcome.ts
|
|
10135
|
-
import { readdirSync as readdirSync10, readFileSync as
|
|
10136
|
-
import { join as
|
|
10419
|
+
import { readdirSync as readdirSync10, readFileSync as readFileSync13 } from "fs";
|
|
10420
|
+
import { join as join17 } from "path";
|
|
10137
10421
|
init_executors();
|
|
10138
10422
|
function endedItself(outcome) {
|
|
10139
10423
|
return outcome === "graceful" || outcome === "budget" || outcome === "aborted";
|
|
@@ -10160,7 +10444,7 @@ function readLatestResultRaw(runLogsDir, runLogKeys) {
|
|
|
10160
10444
|
}
|
|
10161
10445
|
if (!latest) return null;
|
|
10162
10446
|
try {
|
|
10163
|
-
return
|
|
10447
|
+
return readFileSync13(join17(dir, latest.name), "utf8");
|
|
10164
10448
|
} catch {
|
|
10165
10449
|
return null;
|
|
10166
10450
|
}
|
|
@@ -10193,20 +10477,20 @@ init_src();
|
|
|
10193
10477
|
init_git();
|
|
10194
10478
|
|
|
10195
10479
|
// src/deps.ts
|
|
10196
|
-
import { existsSync as
|
|
10197
|
-
import { join as
|
|
10480
|
+
import { existsSync as existsSync12, readdirSync as readdirSync12, readFileSync as readFileSync14 } from "fs";
|
|
10481
|
+
import { join as join20 } from "path";
|
|
10198
10482
|
import { execa } from "execa";
|
|
10199
10483
|
|
|
10200
10484
|
// src/prepareLock.ts
|
|
10201
10485
|
import { createHash } from "crypto";
|
|
10202
10486
|
import { closeSync as closeSync3, openSync as openSync3, rmSync as rmSync2, statSync as statSync9, writeSync } from "fs";
|
|
10203
|
-
import { join as
|
|
10487
|
+
import { join as join19 } from "path";
|
|
10204
10488
|
import { tmpdir } from "os";
|
|
10205
10489
|
var WAIT_TIMEOUT_MS = 5 * 6e4;
|
|
10206
10490
|
var POLL_MS = 300;
|
|
10207
10491
|
var STALE_MS = 20 * 6e4;
|
|
10208
10492
|
function machineLockPath(key, dir = tmpdir()) {
|
|
10209
|
-
return
|
|
10493
|
+
return join19(dir, `swarm-prepare-${createHash("sha1").update(key).digest("hex").slice(0, 16)}.lock`);
|
|
10210
10494
|
}
|
|
10211
10495
|
function defaultTryCreate(path, content) {
|
|
10212
10496
|
try {
|
|
@@ -10280,7 +10564,7 @@ async function runCommand(cmd, worktree) {
|
|
|
10280
10564
|
}
|
|
10281
10565
|
function safeReadText(p) {
|
|
10282
10566
|
try {
|
|
10283
|
-
return
|
|
10567
|
+
return readFileSync14(p, "utf8");
|
|
10284
10568
|
} catch {
|
|
10285
10569
|
return null;
|
|
10286
10570
|
}
|
|
@@ -10320,21 +10604,21 @@ function overridePlan(override) {
|
|
|
10320
10604
|
};
|
|
10321
10605
|
}
|
|
10322
10606
|
function detectPackageManager(worktree, io = {}) {
|
|
10323
|
-
const exists = io.exists ??
|
|
10607
|
+
const exists = io.exists ?? existsSync12;
|
|
10324
10608
|
const readText = io.readText ?? safeReadText;
|
|
10325
|
-
const hasPkgJson = exists(
|
|
10609
|
+
const hasPkgJson = exists(join20(worktree, "package.json"));
|
|
10326
10610
|
if (!hasPkgJson) {
|
|
10327
|
-
if (exists(
|
|
10611
|
+
if (exists(join20(worktree, "deno.json")) || exists(join20(worktree, "deno.jsonc")) || exists(join20(worktree, "deno.lock"))) return "deno";
|
|
10328
10612
|
return null;
|
|
10329
10613
|
}
|
|
10330
|
-
const pkg = readJson(readText,
|
|
10614
|
+
const pkg = readJson(readText, join20(worktree, "package.json"));
|
|
10331
10615
|
const declared = typeof pkg?.packageManager === "string" ? pkg.packageManager.split("@")[0] : void 0;
|
|
10332
10616
|
if (declared === "pnpm" || declared === "bun" || declared === "npm" || declared === "yarn") return declared;
|
|
10333
|
-
if (exists(
|
|
10334
|
-
if (exists(
|
|
10335
|
-
if (exists(
|
|
10336
|
-
if (exists(
|
|
10337
|
-
if (exists(
|
|
10617
|
+
if (exists(join20(worktree, "bun.lock")) || exists(join20(worktree, "bun.lockb"))) return "bun";
|
|
10618
|
+
if (exists(join20(worktree, "pnpm-lock.yaml"))) return "pnpm";
|
|
10619
|
+
if (exists(join20(worktree, "yarn.lock"))) return "yarn";
|
|
10620
|
+
if (exists(join20(worktree, "package-lock.json"))) return "npm";
|
|
10621
|
+
if (exists(join20(worktree, "deno.lock"))) return "deno";
|
|
10338
10622
|
return "npm";
|
|
10339
10623
|
}
|
|
10340
10624
|
function jsInstallCommand(pm) {
|
|
@@ -10359,7 +10643,10 @@ var ECOSYSTEM_MARKERS = [
|
|
|
10359
10643
|
// pyproject сам по себе не говорит, кем ставить — решает лок-файл рядом.
|
|
10360
10644
|
command: null,
|
|
10361
10645
|
refine: [
|
|
10362
|
-
|
|
10646
|
+
// --frozen: ставим ровно по локу и не переписываем его (TAS-572). Без
|
|
10647
|
+
// флага uv доресолвливает лок под pyproject прямо в worktree дрона, и
|
|
10648
|
+
// чужой дифф на полторы сотни строк уезжает в PR по несвязанной задаче.
|
|
10649
|
+
{ file: "uv.lock", ecosystem: "python/uv", command: "uv sync --frozen" },
|
|
10363
10650
|
{ file: "poetry.lock", ecosystem: "python/poetry", command: "poetry install" },
|
|
10364
10651
|
{ file: "requirements.txt", ecosystem: "python/pip", command: "pip install -r requirements.txt" }
|
|
10365
10652
|
]
|
|
@@ -10387,7 +10674,7 @@ var ECOSYSTEM_MARKERS = [
|
|
|
10387
10674
|
// конфигурация — дело сборки, не подготовки
|
|
10388
10675
|
];
|
|
10389
10676
|
function detectPlan(worktree, io) {
|
|
10390
|
-
const exists = io.exists ??
|
|
10677
|
+
const exists = io.exists ?? existsSync12;
|
|
10391
10678
|
const listFiles = io.listFiles ?? safeListFiles;
|
|
10392
10679
|
const pm = detectPackageManager(worktree, io);
|
|
10393
10680
|
if (pm) {
|
|
@@ -10395,7 +10682,7 @@ function detectPlan(worktree, io) {
|
|
|
10395
10682
|
// node_modules на месте — установку пропускаем (быстрый путь переиспользованного worktree).
|
|
10396
10683
|
// У deno каталога может не быть вовсе (кэш в DENO_DIR) — тогда `deno install` отработает
|
|
10397
10684
|
// заново, но он дешёвый: тянет из того же общего кэша.
|
|
10398
|
-
{ label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists:
|
|
10685
|
+
{ label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists: join20(worktree, "node_modules"), source: "detect" }
|
|
10399
10686
|
];
|
|
10400
10687
|
for (const target of workspaceBuildTargets(worktree, io)) {
|
|
10401
10688
|
commands.push({ label: `\u0441\u0431\u043E\u0440\u043A\u0430 ${target.name}`, run: jsRunScript(pm, "build"), cwd: target.dir, source: "detect" });
|
|
@@ -10404,12 +10691,12 @@ function detectPlan(worktree, io) {
|
|
|
10404
10691
|
return { source: "detect", ecosystem: `node/${pm}`, commands };
|
|
10405
10692
|
}
|
|
10406
10693
|
for (const marker of ECOSYSTEM_MARKERS) {
|
|
10407
|
-
const hit = marker.files.some((f) => exists(
|
|
10694
|
+
const hit = marker.files.some((f) => exists(join20(worktree, f))) || marker.ext !== void 0 && listFiles(worktree).some((n) => n.endsWith(marker.ext));
|
|
10408
10695
|
if (!hit) continue;
|
|
10409
10696
|
let ecosystem = marker.ecosystem;
|
|
10410
10697
|
let command = marker.command;
|
|
10411
10698
|
for (const refine of marker.refine ?? []) {
|
|
10412
|
-
if (!exists(
|
|
10699
|
+
if (!exists(join20(worktree, refine.file))) continue;
|
|
10413
10700
|
ecosystem = refine.ecosystem;
|
|
10414
10701
|
command = refine.command;
|
|
10415
10702
|
break;
|
|
@@ -10466,12 +10753,12 @@ var CI_INSTALL_PATTERNS = [
|
|
|
10466
10753
|
function ciWorkflowPlan(worktree, io) {
|
|
10467
10754
|
const readText = io.readText ?? safeReadText;
|
|
10468
10755
|
const listFiles = io.listFiles ?? safeListFiles;
|
|
10469
|
-
const dir =
|
|
10756
|
+
const dir = join20(worktree, ".github", "workflows");
|
|
10470
10757
|
const files = listFiles(dir).filter((n) => n.endsWith(".yml") || n.endsWith(".yaml"));
|
|
10471
10758
|
const seen = /* @__PURE__ */ new Set();
|
|
10472
10759
|
const commands = [];
|
|
10473
10760
|
for (const name of files.sort()) {
|
|
10474
|
-
const yaml = readText(
|
|
10761
|
+
const yaml = readText(join20(dir, name));
|
|
10475
10762
|
if (yaml === null) continue;
|
|
10476
10763
|
for (const raw of workflowRunLines(yaml)) {
|
|
10477
10764
|
const line = raw.replace(/\s+/g, " ").trim();
|
|
@@ -10486,7 +10773,7 @@ function ciWorkflowPlan(worktree, io) {
|
|
|
10486
10773
|
return commands.length ? { source: "ci-workflow", ecosystem: "ci-\u043A\u043E\u043D\u0444\u0438\u0433", commands } : null;
|
|
10487
10774
|
}
|
|
10488
10775
|
function workspaceGlobs(worktree, readText) {
|
|
10489
|
-
const yaml = readText(
|
|
10776
|
+
const yaml = readText(join20(worktree, "pnpm-workspace.yaml"));
|
|
10490
10777
|
if (yaml !== null) {
|
|
10491
10778
|
const globs = [];
|
|
10492
10779
|
let inPackages = false;
|
|
@@ -10506,7 +10793,7 @@ function workspaceGlobs(worktree, readText) {
|
|
|
10506
10793
|
}
|
|
10507
10794
|
if (globs.length) return globs;
|
|
10508
10795
|
}
|
|
10509
|
-
const pkg = readJson(readText,
|
|
10796
|
+
const pkg = readJson(readText, join20(worktree, "package.json"));
|
|
10510
10797
|
const ws = pkg?.workspaces;
|
|
10511
10798
|
const list = Array.isArray(ws) ? ws : Array.isArray(ws?.packages) ? ws.packages : [];
|
|
10512
10799
|
return list.filter((g) => typeof g === "string");
|
|
@@ -10517,14 +10804,14 @@ function workspaceDirs(worktree, globs, io) {
|
|
|
10517
10804
|
if (glob.startsWith("!")) continue;
|
|
10518
10805
|
const star = glob.indexOf("*");
|
|
10519
10806
|
if (star === -1) {
|
|
10520
|
-
const dir =
|
|
10521
|
-
if (io.exists(
|
|
10807
|
+
const dir = join20(worktree, glob);
|
|
10808
|
+
if (io.exists(join20(dir, "package.json"))) dirs.add(dir);
|
|
10522
10809
|
continue;
|
|
10523
10810
|
}
|
|
10524
|
-
const base3 =
|
|
10811
|
+
const base3 = join20(worktree, glob.slice(0, star).replace(/\/$/, ""));
|
|
10525
10812
|
for (const name of io.listDirs(base3)) {
|
|
10526
|
-
const dir =
|
|
10527
|
-
if (io.exists(
|
|
10813
|
+
const dir = join20(base3, name);
|
|
10814
|
+
if (io.exists(join20(dir, "package.json"))) dirs.add(dir);
|
|
10528
10815
|
}
|
|
10529
10816
|
}
|
|
10530
10817
|
return [...dirs];
|
|
@@ -10545,18 +10832,18 @@ function entryPaths(pkg) {
|
|
|
10545
10832
|
return out;
|
|
10546
10833
|
}
|
|
10547
10834
|
function workspaceBuildTargets(worktree, io = {}) {
|
|
10548
|
-
const exists = io.exists ??
|
|
10835
|
+
const exists = io.exists ?? existsSync12;
|
|
10549
10836
|
const readText = io.readText ?? safeReadText;
|
|
10550
10837
|
const listDirs = io.listDirs ?? safeListDirs;
|
|
10551
10838
|
const globs = workspaceGlobs(worktree, readText);
|
|
10552
10839
|
if (!globs.length) return [];
|
|
10553
10840
|
const pkgs = [];
|
|
10554
10841
|
for (const dir of workspaceDirs(worktree, globs, { exists, listDirs })) {
|
|
10555
|
-
const pkg = readJson(readText,
|
|
10842
|
+
const pkg = readJson(readText, join20(dir, "package.json"));
|
|
10556
10843
|
if (!pkg || typeof pkg.name !== "string") continue;
|
|
10557
10844
|
const scripts = pkg.scripts ?? {};
|
|
10558
10845
|
const entries = entryPaths(pkg);
|
|
10559
|
-
const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(
|
|
10846
|
+
const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(join20(dir, e)));
|
|
10560
10847
|
const deps = Object.keys({ ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} });
|
|
10561
10848
|
pkgs.push({ name: pkg.name, dir, deps, needsBuild });
|
|
10562
10849
|
}
|
|
@@ -10584,7 +10871,7 @@ function devDatabaseUrl(worktree, io = {}) {
|
|
|
10584
10871
|
const fromEnv = (io.env ?? process.env).DATABASE_URL;
|
|
10585
10872
|
if (fromEnv && fromEnv.trim()) return fromEnv.trim();
|
|
10586
10873
|
for (const file of [".env", ".env.example"]) {
|
|
10587
|
-
const raw = readText(
|
|
10874
|
+
const raw = readText(join20(worktree, file));
|
|
10588
10875
|
if (raw === null) continue;
|
|
10589
10876
|
const m = /^\s*(?:export\s+)?DATABASE_URL\s*=\s*(.*)$/m.exec(raw);
|
|
10590
10877
|
const value = (m?.[1] ?? "").trim().replace(/^['"]|['"]$/g, "");
|
|
@@ -10600,11 +10887,11 @@ function isLocalDatabase(url) {
|
|
|
10600
10887
|
}
|
|
10601
10888
|
}
|
|
10602
10889
|
function prismaPackages(worktree, io) {
|
|
10603
|
-
const exists = io.exists ??
|
|
10890
|
+
const exists = io.exists ?? existsSync12;
|
|
10604
10891
|
const readText = io.readText ?? safeReadText;
|
|
10605
10892
|
const listDirs = io.listDirs ?? safeListDirs;
|
|
10606
10893
|
const dirs = [worktree, ...workspaceDirs(worktree, workspaceGlobs(worktree, readText), { exists, listDirs })];
|
|
10607
|
-
return dirs.filter((dir) => exists(
|
|
10894
|
+
return dirs.filter((dir) => exists(join20(dir, "prisma", "schema.prisma")) && listDirs(join20(dir, "prisma", "migrations")).length > 0);
|
|
10608
10895
|
}
|
|
10609
10896
|
function jsExecBin(pm, command) {
|
|
10610
10897
|
switch (pm) {
|
|
@@ -10637,7 +10924,7 @@ function planPrepare(worktree, io = {}) {
|
|
|
10637
10924
|
return { ...plan, commands: plan.commands.slice(0, MAX_PLAN_COMMANDS) };
|
|
10638
10925
|
}
|
|
10639
10926
|
async function ensureWorktreeDeps(worktree, io = {}) {
|
|
10640
|
-
const exists = io.exists ??
|
|
10927
|
+
const exists = io.exists ?? existsSync12;
|
|
10641
10928
|
const exec = io.exec ?? ((cmd) => runCommand(cmd, worktree));
|
|
10642
10929
|
const approve = io.approve ?? (() => true);
|
|
10643
10930
|
const plan = planPrepare(worktree, io);
|
|
@@ -10833,6 +11120,10 @@ function isPreStartFailure(run) {
|
|
|
10833
11120
|
function isSpawnParked(run) {
|
|
10834
11121
|
return (run.stop_reason ?? "").includes(SPAWN_PARK_MARK);
|
|
10835
11122
|
}
|
|
11123
|
+
var UNRETRIABLE_PARK_MARK = "\u043F\u043E\u0432\u0442\u043E\u0440 \u043D\u0435 \u043F\u043E\u043C\u043E\u0436\u0435\u0442";
|
|
11124
|
+
function isUnretriableParked(run) {
|
|
11125
|
+
return isPreStartFailure(run) && (run.stop_reason ?? "").includes(UNRETRIABLE_PARK_MARK);
|
|
11126
|
+
}
|
|
10836
11127
|
var SPAWN_ATTEMPT_WINDOW_MS = 60 * 6e4;
|
|
10837
11128
|
function preStartAttempts(runs, taskSlug, opts = {}) {
|
|
10838
11129
|
const windowMs = opts.windowMs ?? SPAWN_ATTEMPT_WINDOW_MS;
|
|
@@ -10846,9 +11137,13 @@ function preStartAttempts(runs, taskSlug, opts = {}) {
|
|
|
10846
11137
|
const at = run.created_at ? stampMs(run.created_at) : Number.NaN;
|
|
10847
11138
|
if (Number.isFinite(at) && now - at >= windowMs) break;
|
|
10848
11139
|
n += 1;
|
|
11140
|
+
if (isUnretriableParked(run)) return Math.max(n, PRE_START_ATTEMPT_LIMIT);
|
|
10849
11141
|
}
|
|
10850
11142
|
return n;
|
|
10851
11143
|
}
|
|
11144
|
+
function lastPreStartAttempt(runs, taskSlug) {
|
|
11145
|
+
return runs.filter((r) => r.task_slug === taskSlug && isPreStartFailure(r)).sort((a, b) => b.id - a.id)[0] ?? null;
|
|
11146
|
+
}
|
|
10852
11147
|
function unresolvedRepoReason(repo, served) {
|
|
10853
11148
|
const raw = repo.trim();
|
|
10854
11149
|
if (!raw || raw.startsWith("/") || raw.startsWith("~") || /^[a-z]:[\\/]/i.test(raw)) return null;
|
|
@@ -10858,6 +11153,9 @@ function unresolvedRepoReason(repo, served) {
|
|
|
10858
11153
|
function parkReason(attempts, reason) {
|
|
10859
11154
|
return `${PRE_START_MARK} ${attempts} \u0440\u0430\u0437\u0430 \u043F\u043E\u0434\u0440\u044F\u0434 \u2014 ${SPAWN_PARK_MARK}, \u0447\u0442\u043E\u0431\u044B \u043D\u0435 \u0441\u043F\u0430\u0432\u043D\u0438\u0442\u044C \u043F\u043E \u043A\u0440\u0443\u0433\u0443. \u0427\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0438\u043B\u043E: ${reason}`;
|
|
10860
11155
|
}
|
|
11156
|
+
function parkReasonUnretriable(reason) {
|
|
11157
|
+
return `${PRE_START_MARK}, \u0438 ${UNRETRIABLE_PARK_MARK} \u2014 ${SPAWN_PARK_MARK} \u0441\u0440\u0430\u0437\u0443, \u043D\u0435 \u0442\u0440\u0430\u0442\u044F ${PRE_START_ATTEMPT_LIMIT} \u043F\u043E\u043F\u044B\u0442\u043A\u0438. \u0427\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0438\u043B\u043E: ${reason}`;
|
|
11158
|
+
}
|
|
10861
11159
|
|
|
10862
11160
|
// src/worktreeReap.ts
|
|
10863
11161
|
init_src();
|
|
@@ -11046,7 +11344,7 @@ async function latestVerdictComment(prUrl, sinceIso, run = realCommentsRunner) {
|
|
|
11046
11344
|
return APPROVE_RE.test(m[1]) ? "approve" : "changes";
|
|
11047
11345
|
});
|
|
11048
11346
|
}
|
|
11049
|
-
async function scanPrComments(prUrl, sinceIso, run,
|
|
11347
|
+
async function scanPrComments(prUrl, sinceIso, run, pick4) {
|
|
11050
11348
|
if (!isGithubPrUrl(prUrl)) return null;
|
|
11051
11349
|
let out;
|
|
11052
11350
|
try {
|
|
@@ -11066,7 +11364,7 @@ async function scanPrComments(prUrl, sinceIso, run, pick3) {
|
|
|
11066
11364
|
const c = comments[i];
|
|
11067
11365
|
if (!c?.body || !c.createdAt) continue;
|
|
11068
11366
|
if (Number.isFinite(sinceMs) && Date.parse(c.createdAt) < sinceMs) continue;
|
|
11069
|
-
const verdict =
|
|
11367
|
+
const verdict = pick4(c.body);
|
|
11070
11368
|
if (verdict == null) continue;
|
|
11071
11369
|
return { verdict, createdAt: c.createdAt };
|
|
11072
11370
|
}
|
|
@@ -11243,65 +11541,270 @@ ${logs.join("\n---\n")}` : header;
|
|
|
11243
11541
|
// src/droneServer.ts
|
|
11244
11542
|
init_droneChannel();
|
|
11245
11543
|
init_taskless();
|
|
11544
|
+
init_toolFailure();
|
|
11246
11545
|
init_attachments();
|
|
11247
11546
|
init_shotUpload();
|
|
11248
11547
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11249
11548
|
import { z } from "zod";
|
|
11250
11549
|
init_git();
|
|
11251
11550
|
|
|
11252
|
-
// src/
|
|
11253
|
-
|
|
11254
|
-
|
|
11255
|
-
|
|
11256
|
-
|
|
11257
|
-
|
|
11258
|
-
|
|
11259
|
-
|
|
11260
|
-
|
|
11261
|
-
|
|
11262
|
-
|
|
11263
|
-
const id = typeof n.id === "string" ? n.id : "";
|
|
11264
|
-
const claim = typeof n.claim === "string" ? n.claim.trim() : "";
|
|
11265
|
-
if (id && claim) out.push({ id, claim });
|
|
11266
|
-
}
|
|
11267
|
-
return out;
|
|
11551
|
+
// src/diff.ts
|
|
11552
|
+
import { statSync as statSync10 } from "fs";
|
|
11553
|
+
import { join as join25 } from "path";
|
|
11554
|
+
import { $ as $4 } from "execa";
|
|
11555
|
+
var MAX_FILES = 200;
|
|
11556
|
+
var MAX_LINES_PER_FILE = 1500;
|
|
11557
|
+
var MAX_UNTRACKED = 60;
|
|
11558
|
+
function cleanPath(raw) {
|
|
11559
|
+
let p = raw.trim();
|
|
11560
|
+
if (p.startsWith('"') && p.endsWith('"') && p.length >= 2) p = p.slice(1, -1);
|
|
11561
|
+
return p;
|
|
11268
11562
|
}
|
|
11269
|
-
function
|
|
11270
|
-
|
|
11563
|
+
function stripAB(raw) {
|
|
11564
|
+
const p = cleanPath(raw);
|
|
11565
|
+
if (p === "/dev/null") return p;
|
|
11566
|
+
if (p.startsWith("a/") || p.startsWith("b/")) return p.slice(2);
|
|
11567
|
+
return p;
|
|
11271
11568
|
}
|
|
11272
|
-
function
|
|
11273
|
-
const
|
|
11274
|
-
if (!
|
|
11275
|
-
const
|
|
11276
|
-
|
|
11277
|
-
|
|
11278
|
-
|
|
11279
|
-
|
|
11280
|
-
|
|
11281
|
-
|
|
11282
|
-
|
|
11283
|
-
|
|
11284
|
-
|
|
11569
|
+
function parseUnifiedDiff(text) {
|
|
11570
|
+
const files = [];
|
|
11571
|
+
if (!text) return files;
|
|
11572
|
+
const lines = text.split("\n");
|
|
11573
|
+
let f = null;
|
|
11574
|
+
let hunk = null;
|
|
11575
|
+
let oldNo = 0;
|
|
11576
|
+
let newNo = 0;
|
|
11577
|
+
const push = () => {
|
|
11578
|
+
if (f) files.push(f);
|
|
11579
|
+
};
|
|
11580
|
+
for (const line of lines) {
|
|
11581
|
+
if (line.startsWith("diff --git ")) {
|
|
11582
|
+
push();
|
|
11583
|
+
hunk = null;
|
|
11584
|
+
const m = line.match(/^diff --git (.+) (.+)$/);
|
|
11585
|
+
const guess = m ? stripAB(m[2]) : "";
|
|
11586
|
+
f = { path: guess, oldPath: null, status: "modified", added: 0, removed: 0, binary: false, hunks: [] };
|
|
11587
|
+
continue;
|
|
11588
|
+
}
|
|
11589
|
+
if (!f) continue;
|
|
11590
|
+
if (line.startsWith("new file mode")) {
|
|
11591
|
+
f.status = "added";
|
|
11592
|
+
continue;
|
|
11593
|
+
}
|
|
11594
|
+
if (line.startsWith("deleted file mode")) {
|
|
11595
|
+
f.status = "deleted";
|
|
11596
|
+
continue;
|
|
11597
|
+
}
|
|
11598
|
+
if (line.startsWith("rename from ") || line.startsWith("copy from ")) {
|
|
11599
|
+
f.oldPath = cleanPath(line.replace(/^(rename|copy) from /, ""));
|
|
11600
|
+
f.status = "renamed";
|
|
11601
|
+
continue;
|
|
11602
|
+
}
|
|
11603
|
+
if (line.startsWith("rename to ") || line.startsWith("copy to ")) {
|
|
11604
|
+
f.path = cleanPath(line.replace(/^(rename|copy) to /, ""));
|
|
11605
|
+
f.status = "renamed";
|
|
11606
|
+
continue;
|
|
11607
|
+
}
|
|
11608
|
+
if (line.startsWith("Binary files ")) {
|
|
11609
|
+
f.binary = true;
|
|
11610
|
+
continue;
|
|
11611
|
+
}
|
|
11612
|
+
if (line.startsWith("--- ")) {
|
|
11613
|
+
const p = stripAB(line.slice(4));
|
|
11614
|
+
if (p === "/dev/null") f.status = "added";
|
|
11615
|
+
continue;
|
|
11616
|
+
}
|
|
11617
|
+
if (line.startsWith("+++ ")) {
|
|
11618
|
+
const p = stripAB(line.slice(4));
|
|
11619
|
+
if (p === "/dev/null") f.status = "deleted";
|
|
11620
|
+
else if (p) f.path = p;
|
|
11621
|
+
continue;
|
|
11622
|
+
}
|
|
11623
|
+
if (line.startsWith("@@")) {
|
|
11624
|
+
const m = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@(.*)$/);
|
|
11625
|
+
if (m) {
|
|
11626
|
+
oldNo = Number(m[1]);
|
|
11627
|
+
newNo = Number(m[2]);
|
|
11628
|
+
hunk = { header: (m[3] ?? "").trim(), lines: [] };
|
|
11629
|
+
f.hunks.push(hunk);
|
|
11630
|
+
}
|
|
11631
|
+
continue;
|
|
11632
|
+
}
|
|
11633
|
+
if (!hunk) continue;
|
|
11634
|
+
if (line.startsWith("\\")) continue;
|
|
11635
|
+
const tag = line[0];
|
|
11636
|
+
const body = line.slice(1);
|
|
11637
|
+
if (tag === "+") {
|
|
11638
|
+
hunk.lines.push({ k: "add", o: null, n: newNo++, t: body });
|
|
11639
|
+
f.added++;
|
|
11640
|
+
} else if (tag === "-") {
|
|
11641
|
+
hunk.lines.push({ k: "del", o: oldNo++, n: null, t: body });
|
|
11642
|
+
f.removed++;
|
|
11643
|
+
} else if (tag === " ") {
|
|
11644
|
+
hunk.lines.push({ k: "ctx", o: oldNo++, n: newNo++, t: body });
|
|
11645
|
+
}
|
|
11646
|
+
}
|
|
11647
|
+
push();
|
|
11648
|
+
return files;
|
|
11285
11649
|
}
|
|
11286
|
-
function
|
|
11287
|
-
|
|
11288
|
-
const
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
if (
|
|
11292
|
-
|
|
11293
|
-
|
|
11650
|
+
function truncateFile(f) {
|
|
11651
|
+
let count = 0;
|
|
11652
|
+
const hunks = [];
|
|
11653
|
+
let cut = false;
|
|
11654
|
+
for (const h of f.hunks) {
|
|
11655
|
+
if (count >= MAX_LINES_PER_FILE) {
|
|
11656
|
+
cut = true;
|
|
11657
|
+
break;
|
|
11658
|
+
}
|
|
11659
|
+
const room = MAX_LINES_PER_FILE - count;
|
|
11660
|
+
if (h.lines.length <= room) {
|
|
11661
|
+
hunks.push(h);
|
|
11662
|
+
count += h.lines.length;
|
|
11663
|
+
} else {
|
|
11664
|
+
hunks.push({ header: h.header, lines: h.lines.slice(0, room) });
|
|
11665
|
+
count += room;
|
|
11666
|
+
cut = true;
|
|
11667
|
+
break;
|
|
11668
|
+
}
|
|
11294
11669
|
}
|
|
11295
|
-
return
|
|
11670
|
+
return cut ? { ...f, hunks, truncated: true } : f;
|
|
11296
11671
|
}
|
|
11297
|
-
|
|
11298
|
-
|
|
11299
|
-
|
|
11300
|
-
|
|
11672
|
+
async function resolveBase(wt, baseBranch) {
|
|
11673
|
+
const tryRef = async (ref) => {
|
|
11674
|
+
const sha = (await $4({ reject: false })`git -C ${wt} rev-parse --verify --quiet ${ref + "^{commit}"}`).stdout.trim();
|
|
11675
|
+
if (!sha) return null;
|
|
11676
|
+
const mb2 = (await $4({ reject: false })`git -C ${wt} merge-base HEAD ${ref}`).stdout.trim();
|
|
11677
|
+
return mb2 || null;
|
|
11678
|
+
};
|
|
11679
|
+
const originHead = (await $4({ reject: false })`git -C ${wt} symbolic-ref --quiet --short refs/remotes/origin/HEAD`).stdout.trim();
|
|
11680
|
+
const candidates = [
|
|
11681
|
+
...baseBranch ? [`origin/${baseBranch}`, baseBranch] : [],
|
|
11682
|
+
originHead,
|
|
11683
|
+
"origin/main",
|
|
11684
|
+
"origin/master",
|
|
11685
|
+
"main",
|
|
11686
|
+
"master"
|
|
11687
|
+
].filter(Boolean);
|
|
11688
|
+
for (const c of candidates) {
|
|
11689
|
+
const mb2 = await tryRef(c);
|
|
11690
|
+
if (mb2) return mb2;
|
|
11691
|
+
}
|
|
11692
|
+
return null;
|
|
11693
|
+
}
|
|
11694
|
+
function mtime(path) {
|
|
11695
|
+
try {
|
|
11696
|
+
return statSync10(path).mtimeMs;
|
|
11697
|
+
} catch {
|
|
11698
|
+
return 0;
|
|
11699
|
+
}
|
|
11700
|
+
}
|
|
11701
|
+
async function collectRunDiff(worktree, opts = {}) {
|
|
11702
|
+
const stub = (reason) => ({ ok: false, reason, pr: opts.pr ?? null, files: [], added: 0, removed: 0 });
|
|
11703
|
+
const gone = opts.status === "queued" ? "\u0434\u0440\u043E\u043D \u0435\u0449\u0451 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u2014 \u043A\u043E\u0434\u0430 \u043F\u043E\u043A\u0430 \u043D\u0435\u0442" : "worktree \u0443\u0431\u0440\u0430\u043D \u2014 \u043A\u043E\u0434 \u0441\u043C\u043E\u0442\u0440\u0438 \u0432 PR";
|
|
11704
|
+
if (!worktree) return stub(gone);
|
|
11705
|
+
const isWt = (await $4({ reject: false })`git -C ${worktree} rev-parse --is-inside-work-tree`).stdout.trim();
|
|
11706
|
+
if (isWt !== "true") return stub(gone);
|
|
11707
|
+
const base3 = await resolveBase(worktree, opts.baseBranch);
|
|
11708
|
+
const from = base3 ?? "HEAD";
|
|
11709
|
+
const trackedTxt = (await $4({ reject: false })`git -C ${worktree} diff --no-color --find-renames ${from} --`).stdout;
|
|
11710
|
+
let files = parseUnifiedDiff(trackedTxt);
|
|
11711
|
+
const untrackedRaw = (await $4({ reject: false })`git -C ${worktree} ls-files --others --exclude-standard -z`).stdout;
|
|
11712
|
+
const untracked = untrackedRaw.split("\0").filter(Boolean).slice(0, MAX_UNTRACKED);
|
|
11713
|
+
for (const rel of untracked) {
|
|
11714
|
+
const t = (await $4({ reject: false })`git -C ${worktree} diff --no-color --no-index -- /dev/null ${rel}`).stdout;
|
|
11715
|
+
for (const pf of parseUnifiedDiff(t)) {
|
|
11716
|
+
pf.status = "added";
|
|
11717
|
+
if (!pf.path || pf.path === "/dev/null") pf.path = rel;
|
|
11718
|
+
files.push(pf);
|
|
11719
|
+
}
|
|
11720
|
+
}
|
|
11721
|
+
files.sort((a, b) => mtime(join25(worktree, b.path)) - mtime(join25(worktree, a.path)));
|
|
11722
|
+
const truncatedFiles = files.length > MAX_FILES;
|
|
11723
|
+
if (truncatedFiles) files = files.slice(0, MAX_FILES);
|
|
11724
|
+
const added = files.reduce((s, f) => s + f.added, 0);
|
|
11725
|
+
const removed = files.reduce((s, f) => s + f.removed, 0);
|
|
11726
|
+
const out = files.map(truncateFile);
|
|
11727
|
+
return {
|
|
11728
|
+
ok: true,
|
|
11729
|
+
base: base3 ? base3.slice(0, 9) : null,
|
|
11730
|
+
files: out,
|
|
11731
|
+
added,
|
|
11732
|
+
removed,
|
|
11733
|
+
...truncatedFiles ? { truncated: true } : {}
|
|
11734
|
+
};
|
|
11735
|
+
}
|
|
11736
|
+
async function changedPaths(worktree, opts = {}) {
|
|
11737
|
+
if (!worktree) return [];
|
|
11738
|
+
try {
|
|
11739
|
+
const isWt = (await $4({ reject: false })`git -C ${worktree} rev-parse --is-inside-work-tree`).stdout.trim();
|
|
11740
|
+
if (isWt !== "true") return [];
|
|
11741
|
+
const from = await resolveBase(worktree, opts.baseBranch) ?? "HEAD";
|
|
11742
|
+
const tracked = (await $4({ reject: false })`git -C ${worktree} diff --name-only --find-renames ${from} --`).stdout;
|
|
11743
|
+
const untracked = (await $4({ reject: false })`git -C ${worktree} ls-files --others --exclude-standard -z`).stdout;
|
|
11744
|
+
const all = [...tracked.split("\n"), ...untracked.split("\0")].map((s) => s.trim()).filter(Boolean);
|
|
11745
|
+
return [...new Set(all)].sort();
|
|
11746
|
+
} catch {
|
|
11747
|
+
return [];
|
|
11748
|
+
}
|
|
11749
|
+
}
|
|
11750
|
+
|
|
11751
|
+
// src/zoneNotes.ts
|
|
11752
|
+
init_src();
|
|
11753
|
+
function parseZoneMemory(raw, zone) {
|
|
11754
|
+
const key = normalizeZoneLabel(zone);
|
|
11755
|
+
if (!key || !raw || typeof raw !== "object" || Array.isArray(raw)) return [];
|
|
11756
|
+
const notes = raw[key];
|
|
11757
|
+
if (!Array.isArray(notes)) return [];
|
|
11758
|
+
const out = [];
|
|
11759
|
+
for (const item of notes) {
|
|
11760
|
+
if (!item || typeof item !== "object") continue;
|
|
11761
|
+
const n = item;
|
|
11762
|
+
const id = typeof n.id === "string" ? n.id : "";
|
|
11763
|
+
const claim = typeof n.claim === "string" ? n.claim.trim() : "";
|
|
11764
|
+
if (id && claim) out.push({ id, claim });
|
|
11765
|
+
}
|
|
11766
|
+
return out;
|
|
11767
|
+
}
|
|
11768
|
+
function normalizeZoneLabel(zone) {
|
|
11769
|
+
return (zone ?? "").trim().replace(/^area:/i, "").trim().toLowerCase();
|
|
11770
|
+
}
|
|
11771
|
+
function renderZoneMemoryBlock(zone, notes) {
|
|
11772
|
+
const label2 = normalizeZoneLabel(zone);
|
|
11773
|
+
if (!label2) return "";
|
|
11774
|
+
const head = notes.length ? [
|
|
11775
|
+
`\u{1F9E0} \u041F\u0410\u041C\u042F\u0422\u042C \u0417\u041E\u041D\u042B \`${label2}\` \u2014 \u0447\u0442\u043E \u0440\u043E\u0439 \u0443\u0436\u0435 \u0432\u044B\u044F\u0441\u043D\u0438\u043B \u043F\u0440\u043E \u044D\u0442\u043E\u0442 \u043A\u0443\u0441\u043E\u043A \u043A\u043E\u0434\u0430 (\u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u043E\u043C, \u043D\u0435 \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0437 \u043A\u043E\u0434\u0430):`,
|
|
11776
|
+
...notes.map((n) => `\u2022 [${n.id}] ${n.claim}`),
|
|
11777
|
+
"\u042D\u0442\u043E \u043F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0438, \u0430 \u043D\u0435 \u0438\u0441\u0442\u0438\u043D\u0430 \u0432 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0439 \u0438\u043D\u0441\u0442\u0430\u043D\u0446\u0438\u0438: \u0440\u0430\u0437\u043E\u0448\u043B\u043E\u0441\u044C \u0441 \u043A\u043E\u0434\u043E\u043C \u2014 \u0432\u0435\u0440\u044C \u043A\u043E\u0434\u0443 \u0438 \u0441\u043A\u0430\u0436\u0438 \u043E\u0431 \u044D\u0442\u043E\u043C \u0432 \u0441\u0434\u0430\u0447\u0435.",
|
|
11778
|
+
`\u041F\u0440\u0438\u0433\u043E\u0434\u0438\u043B\u0430\u0441\u044C \u0437\u0430\u043C\u0435\u0442\u043A\u0430 \u2014 \u043D\u0430\u0437\u043E\u0432\u0438 \u0435\u0451 \u0432 done: \`zone_notes_used: ["${notes[0].id}"]\` (id \u0438\u0437 \u0441\u043F\u0438\u0441\u043A\u0430 \u0432\u044B\u0448\u0435 \u0438\u043B\u0438 \u0441\u0430\u043C \u0442\u0435\u043A\u0441\u0442 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F). \u042D\u0442\u043E \u0435\u0434\u0438\u043D\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0435, \u0447\u0435\u043C \u0437\u0430\u043C\u0435\u0442\u043A\u0430 \u0436\u0438\u0432\u0451\u0442: \u043F\u0430\u043C\u044F\u0442\u044C \u0437\u043E\u043D\u044B \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u0430, \u0438 \u0432\u044B\u0442\u0435\u0441\u043D\u044F\u0435\u0442\u0441\u044F \u0442\u0430, \u0432 \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u0440\u043E\u0439 \u0434\u043E\u043B\u044C\u0448\u0435 \u0432\u0441\u0435\u0445 \u043D\u0435 \u0443\u0431\u0435\u0436\u0434\u0430\u043B\u0441\u044F. \u041D\u0435 \u043F\u0440\u0438\u0433\u043E\u0434\u0438\u043B\u043E\u0441\u044C \u043D\u0438\u0447\u0435\u0433\u043E \u2014 \u043D\u0435 \u043D\u0430\u0437\u044B\u0432\u0430\u0439 \u043D\u0438\u0447\u0435\u0433\u043E, \u044D\u0442\u043E \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0438\u0441\u0445\u043E\u0434.`
|
|
11779
|
+
] : [`\u{1F9E0} \u041F\u0410\u041C\u042F\u0422\u042C \u0417\u041E\u041D\u042B \`${label2}\` \u043F\u0443\u0441\u0442\u0430 \u2014 \u0440\u043E\u0439 \u043F\u0440\u043E \u044D\u0442\u043E\u0442 \u043A\u0443\u0441\u043E\u043A \u043A\u043E\u0434\u0430 \u043F\u043E\u043A\u0430 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B.`];
|
|
11780
|
+
return [
|
|
11781
|
+
...head,
|
|
11782
|
+
`\u{1F9E0} \u0417\u0410\u041C\u0415\u0422\u041A\u0410 \u0412 \u041F\u0410\u041C\u042F\u0422\u042C \u0417\u041E\u041D\u042B \u2014 \u0432 \u043A\u043E\u043D\u0446\u0435 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0438 \u0434\u043E ${ZONE_NOTES_PER_RUN_MAX} \u0448\u0442\u0443\u043A: \`zone_notes: [{ zone: "${label2}", claim: "\u2026", why: "\u2026" }]\` (zone \u043C\u043E\u0436\u043D\u043E \u043D\u0435 \u0443\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C \u2014 \u0432\u043E\u0437\u044C\u043C\u0451\u0442\u0441\u044F \u0442\u0432\u043E\u044F). \u0427\u0422\u041E \u0433\u043E\u0434\u0438\u0442\u0441\u044F: \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u0441 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439 (\xAB\u043E\u0447\u0435\u0440\u0435\u0434\u044C \u0447\u0438\u0442\u0430\u044E\u0442 \u0438\u0437 \u043E\u0434\u043D\u043E\u0439 \u0442\u043E\u0447\u043A\u0438, \u043F\u043E\u0442\u043E\u043C\u0443 \u0447\u0442\u043E \u2026\xBB), \u0433\u0440\u0430\u0431\u043B\u0438 \u0441 \u0446\u0435\u043D\u043E\u0439 (\xAB\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043D\u0430\u043A\u0430\u0442\u044B\u0432\u0430\u044E\u0442\u0441\u044F \u043D\u0435 \u0442\u0435\u043C, \u0447\u0435\u043C \u043A\u0430\u0436\u0435\u0442\u0441\u044F, \u2014 \u0441\u0442\u043E\u0438\u043B\u043E \u0447\u0430\u0441\u0430\xBB), \u043E\u0442\u0431\u0440\u043E\u0448\u0435\u043D\u043D\u044B\u0439 \u0432\u0430\u0440\u0438\u0430\u043D\u0442 \u0441 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439. \u0427\u0422\u041E \u041D\u0415 \u0433\u043E\u0434\u0438\u0442\u0441\u044F: \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u043E \u043A\u043E\u0434\u0430 (\xAB\u0445\u0435\u043D\u0434\u043B\u0435\u0440 \u043B\u0435\u0436\u0438\u0442 \u0432 handlers.ts\xBB, \xAB\u044D\u0442\u0430 \u0444\u0443\u043D\u043A\u0446\u0438\u044F \u0434\u0435\u043B\u0430\u0435\u0442 \u0442\u043E-\u0442\u043E\xBB) \u2014 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0434\u0440\u043E\u043D \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0435\u0442 \u044D\u0442\u043E \u0441\u0430\u043C \u0438 \u0431\u044B\u0441\u0442\u0440\u0435\u0435, \u0447\u0435\u043C \u0442\u0432\u043E\u044E \u0437\u0430\u043C\u0435\u0442\u043A\u0443. \u0420\u0435\u0448\u0430\u0435\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440; \u043D\u0438\u0447\u0435\u0433\u043E \u0442\u0430\u043A\u043E\u0433\u043E \u043D\u0435 \u0431\u044B\u043B\u043E \u2014 \u043D\u0435 \u0432\u044B\u0434\u0443\u043C\u044B\u0432\u0430\u0439, \u043F\u0443\u0441\u0442\u043E\u0439 \u0441\u043F\u0438\u0441\u043E\u043A \u044D\u0442\u043E \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0438\u0441\u0445\u043E\u0434 \u043F\u0440\u043E\u0433\u043E\u043D\u0430.`
|
|
11783
|
+
].join("\n");
|
|
11784
|
+
}
|
|
11785
|
+
function collectZoneNoteConfirmations(report) {
|
|
11786
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11787
|
+
const out = [];
|
|
11788
|
+
for (const raw of report.used ?? []) {
|
|
11789
|
+
const s = typeof raw === "string" ? raw.trim() : "";
|
|
11790
|
+
if (!s || seen.has(s)) continue;
|
|
11791
|
+
seen.add(s);
|
|
11792
|
+
out.push(s);
|
|
11793
|
+
}
|
|
11794
|
+
return out;
|
|
11795
|
+
}
|
|
11796
|
+
|
|
11797
|
+
// src/droneServer.ts
|
|
11798
|
+
init_src();
|
|
11799
|
+
init_src();
|
|
11800
|
+
|
|
11301
11801
|
// src/reviewCycle.ts
|
|
11302
11802
|
init_src();
|
|
11803
|
+
function needsFixIteration(run) {
|
|
11804
|
+
return run.review_verdict === "changes" || isNegativeVerifyVerdict(toVerifyVerdict(run.verify_verdict));
|
|
11805
|
+
}
|
|
11303
11806
|
function reviewTransitionPatch(run, prUrl) {
|
|
11304
|
-
const isFixIteration =
|
|
11807
|
+
const isFixIteration = needsFixIteration(run);
|
|
11305
11808
|
if (isFixIteration) {
|
|
11306
11809
|
const cycle = (run.review_cycle ?? 0) + 1;
|
|
11307
11810
|
return {
|
|
@@ -11336,6 +11839,18 @@ function applyReviewTransition(db, run, prUrl) {
|
|
|
11336
11839
|
|
|
11337
11840
|
// src/droneServer.ts
|
|
11338
11841
|
init_src();
|
|
11842
|
+
function readTaskFailureInstruction(slug, failure) {
|
|
11843
|
+
const why = describeToolFailure(failure);
|
|
11844
|
+
if (TOOL_FAILURE_RETRIABLE[failure.code]) {
|
|
11845
|
+
return `\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0435\u0440\u0435\u0447\u0438\u0442\u0430\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 ${slug}: ${why}. \u042D\u0442\u043E \u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E \u0438 \u043B\u0435\u0447\u0438\u0442\u0441\u044F \u043F\u043E\u0432\u0442\u043E\u0440\u043E\u043C \u2014 \u0440\u0430\u0431\u043E\u0442\u0430\u0439 \u043F\u043E\u043A\u0430 \u043F\u043E \u0442\u043E\u043C\u0443, \u0447\u0442\u043E \u0443\u0436\u0435 \u0432 \u043F\u0440\u043E\u043C\u043F\u0442\u0435, \u0438 \u043F\u043E\u0437\u043E\u0432\u0438 read_task \u0435\u0449\u0451 \u0440\u0430\u0437 \u043F\u043E\u0437\u0436\u0435 (\u043F\u0435\u0440\u0435\u0434 \u0441\u0434\u0430\u0447\u0435\u0439 \u2014 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E).`;
|
|
11846
|
+
}
|
|
11847
|
+
return [
|
|
11848
|
+
`\u26D4 \u0417\u0410\u0414\u0410\u0427\u0423 ${slug} \u041F\u0420\u041E\u0427\u0418\u0422\u0410\u0422\u042C \u041D\u0415\u041B\u042C\u0417\u042F: ${why}.`,
|
|
11849
|
+
"\u041F\u043E\u0432\u0442\u043E\u0440 \u0442\u0443\u0442 \u043D\u0435 \u043F\u043E\u043C\u043E\u0436\u0435\u0442 \u2014 \u043F\u0440\u0438\u0447\u0438\u043D\u0430 \u043D\u0435 \u0432 \u0441\u0435\u0442\u0438, \u043E\u043D\u0430 \u0441\u0430\u043C\u0430 \u043D\u0435 \u0440\u0430\u0437\u043E\u0439\u0434\u0451\u0442\u0441\u044F.",
|
|
11850
|
+
"\u041D\u0415 \u0440\u0430\u0431\u043E\u0442\u0430\u0439 \u043F\u043E \u0441\u043B\u0430\u0433\u0443 \u0438 \u043F\u043E \u043F\u0430\u043C\u044F\u0442\u0438: \u043F\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430, \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u0442\u044B \u043D\u0435 \u0432\u0438\u0434\u0435\u043B, \u043D\u0435 \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0438\u0437 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u0447\u0438 (TASWM-288).",
|
|
11851
|
+
"\u0421\u043F\u0440\u043E\u0441\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 ask() \u2014 \u043D\u0430\u0437\u043E\u0432\u0438 \u0435\u043C\u0443 \u044D\u0442\u0443 \u043F\u0440\u0438\u0447\u0438\u043D\u0443 \u0434\u043E\u0441\u043B\u043E\u0432\u043D\u043E \u2014 \u0438 \u0434\u0430\u043B\u044C\u0448\u0435 \u0434\u0435\u043B\u0430\u0439 \u0442\u043E, \u0447\u0442\u043E \u043E\u043D \u043E\u0442\u0432\u0435\u0442\u0438\u0442."
|
|
11852
|
+
].join("\n");
|
|
11853
|
+
}
|
|
11339
11854
|
function freezeInstruction(branch, reason = "\u0435\u0433\u043E \u043F\u0443\u043B\u044C\u0441 (heartbeat) \u0443\u0441\u0442\u0430\u0440\u0435\u043B") {
|
|
11340
11855
|
return [
|
|
11341
11856
|
`\u26D4 OVERMIND \u041D\u0415\u0414\u041E\u0421\u0422\u0423\u041F\u0415\u041D \u2014 ${reason}. \u041D\u0430\u0434\u0437\u043E\u0440\u0430 \u043D\u0430\u0434 \u0442\u043E\u0431\u043E\u0439 \u0441\u0435\u0439\u0447\u0430\u0441 \u043D\u0435\u0442: \u043D\u0435\u043A\u043E\u043C\u0443`,
|
|
@@ -11433,16 +11948,17 @@ async function buildDroneServer(store, opts = {}) {
|
|
|
11433
11948
|
mismatchNote = `\u26A0\uFE0F \u0434\u0435\u043C\u043E\u043D \u0447\u0438\u0441\u043B\u0438\u0442 \u0442\u0432\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u043A\u0430\u043A \xAB${run.task_slug}\xBB \u2014 \u044D\u0442\u043E \u041D\u0415 \u0442\u0432\u043E\u044F \u0437\u0430\u0434\u0430\u0447\u0430 (TASWM-211). \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D \u0434\u043E\u0432\u0435\u0440\u0435\u043D\u043D\u044B\u0439 slug \u0438\u0437 \u0441\u043F\u0430\u0432\u043D\u0430.`;
|
|
11434
11949
|
}
|
|
11435
11950
|
if (!slug) return { content: [{ type: "text", text: "\u043F\u0440\u043E\u0433\u043E\u043D \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D" }], isError: true };
|
|
11436
|
-
const
|
|
11437
|
-
if (!
|
|
11438
|
-
const
|
|
11951
|
+
const read = await opts.taskless.getTaskDetailed(slug);
|
|
11952
|
+
if (!read.ok) {
|
|
11953
|
+
const failText = readTaskFailureInstruction(slug, read.failure);
|
|
11439
11954
|
return {
|
|
11440
11955
|
content: [{ type: "text", text: mismatchNote ? `${mismatchNote}
|
|
11441
11956
|
|
|
11442
|
-
${
|
|
11957
|
+
${failText}` : failText }],
|
|
11443
11958
|
isError: true
|
|
11444
11959
|
};
|
|
11445
11960
|
}
|
|
11961
|
+
const task = read.task;
|
|
11446
11962
|
const context = await fetchTaskContext(opts.taskless, task);
|
|
11447
11963
|
const attachments = await prepareTaskAttachments({
|
|
11448
11964
|
worktree: opts.worktree ?? process.cwd(),
|
|
@@ -11530,19 +12046,36 @@ ${notFoundText}` : notFoundText }],
|
|
|
11530
12046
|
);
|
|
11531
12047
|
server.tool(
|
|
11532
12048
|
"return_task",
|
|
11533
|
-
"\u0412\u0435\u0440\u043D\u0443\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u0410\u0412\u0422\u041E\u0420\u0423 \u043D\u0430 \u0443\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u0435 \u0438 \u0437\u0430\u043A\u0440\u044B\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D (\u0441\u043B\u043E\u0442 \u043E\u0441\u0432\u043E\u0431\u043E\u0434\u0438\u0442\u0441\u044F, \u0440\u043E\u0439 \u0432\u043E\u0437\u044C\u043C\u0451\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0443\u044E \u0437\u0430\u0434\u0430\u0447\u0443). \u0417\u043E\u0432\u0438, \u0422\u041E\u041B\u042C\u041A\u041E \u0435\u0441\u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435\u043B\u044C\u0437\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u043D\u0438 \u0432 \u043E\u0434\u043D\u043E\u043C \u0440\u0430\u0437\u0443\u043C\u043D\u043E\u043C \u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u0438 \u0418\u041B\u0418 \u043D\u0435\u043F\u043E\u043D\u044F\u0442\u043D\u043E, \u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442. \u0414\u0432\u0430 \u043E\u0441\u043C\u044B\u0441\u043B\u0435\u043D\u043D\u044B\u0445 \u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u044F \u2014 \u041D\u0415 \u043F\u043E\u0432\u043E\u0434: \u0432\u044B\u0431\u0435\u0440\u0438 \u043E\u0447\u0435\u0432\u0438\u0434\u043D\u043E\u0435, \u0441\u0434\u0435\u043B\u0430\u0439 \u0438 \u043D\u0430\u0437\u043E\u0432\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0443 \u0432 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0438 PR. \u041F\u043E\u043D\u044F\u0442\u043D\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430, \u043A\u043E\u0442\u043E\u0440\u0430\u044F \u0432 \u043E\u0434\u043D\u043E\u043C \u043F\u0443\u043D\u043A\u0442\u0435 \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0442 \u043F\u0440\u0430\u0432\u0438\u043B\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430, \u2014 \u0442\u043E\u0436\u0435 \u043D\u0435 \u043F\u043E\u0432\u043E\u0434: \u044D\u0442\u043E ask. \u0417\u0430\u0434\u0430\u0447\u0430 \u0443\u0439\u0434\u0451\u0442 \u0430\u0432\u0442\u043E\u0440\u0443 (\u043D\u0430\u0437\u043D\u0430\u0447\u0438\u043C \u043D\u0430 \u043D\u0435\u0433\u043E \u0438 \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0451\u043C \u0432 \u0440\u0430\u0431\u043E\u0442\u0443), \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0443\u0435\u0434\u0443\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u043E\u0442 \u0442\u0432\u043E\u0435\u0433\u043E \u0438\u043C\u0435\u043D\u0438. \u041F\u043E\u0441\u043B\u0435 \u0432\u044B\u0437\u043E\u0432\u0430 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0437\u043E\u0432\u0438 \u2014 \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0441\u0435\u0441\u0441\u0438\u044E.",
|
|
12049
|
+
"\u0412\u0435\u0440\u043D\u0443\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u0410\u0412\u0422\u041E\u0420\u0423 \u043D\u0430 \u0443\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u0435 \u0438 \u0437\u0430\u043A\u0440\u044B\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D (\u0441\u043B\u043E\u0442 \u043E\u0441\u0432\u043E\u0431\u043E\u0434\u0438\u0442\u0441\u044F, \u0440\u043E\u0439 \u0432\u043E\u0437\u044C\u043C\u0451\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0443\u044E \u0437\u0430\u0434\u0430\u0447\u0443). \u0417\u043E\u0432\u0438, \u0422\u041E\u041B\u042C\u041A\u041E \u0435\u0441\u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435\u043B\u044C\u0437\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u043D\u0438 \u0432 \u043E\u0434\u043D\u043E\u043C \u0440\u0430\u0437\u0443\u043C\u043D\u043E\u043C \u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u0438 \u0418\u041B\u0418 \u043D\u0435\u043F\u043E\u043D\u044F\u0442\u043D\u043E, \u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442. \u0414\u0432\u0430 \u043E\u0441\u043C\u044B\u0441\u043B\u0435\u043D\u043D\u044B\u0445 \u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u044F \u2014 \u041D\u0415 \u043F\u043E\u0432\u043E\u0434: \u0432\u044B\u0431\u0435\u0440\u0438 \u043E\u0447\u0435\u0432\u0438\u0434\u043D\u043E\u0435, \u0441\u0434\u0435\u043B\u0430\u0439 \u0438 \u043D\u0430\u0437\u043E\u0432\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0443 \u0432 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0438 PR. \u041F\u043E\u043D\u044F\u0442\u043D\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430, \u043A\u043E\u0442\u043E\u0440\u0430\u044F \u0432 \u043E\u0434\u043D\u043E\u043C \u043F\u0443\u043D\u043A\u0442\u0435 \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0442 \u043F\u0440\u0430\u0432\u0438\u043B\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430, \u2014 \u0442\u043E\u0436\u0435 \u043D\u0435 \u043F\u043E\u0432\u043E\u0434: \u044D\u0442\u043E ask. \u041E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0439 \u0437\u0430\u043A\u043E\u043D\u043D\u044B\u0439 \u0441\u043B\u0443\u0447\u0430\u0439 \u2014 \u0437\u0430\u0434\u0430\u0447\u0430 \u0421\u041B\u0418\u0428\u041A\u041E\u041C \u0412\u0415\u041B\u0418\u041A\u0410 (reason=too_big): \u043F\u043E\u043D\u044F\u0442\u043D\u0430 \u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u0430, \u043D\u043E \u044D\u0442\u043E \u043B\u0438\u0441\u0442 \u043D\u0430 \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u043F\u0440\u043E\u0433\u043E\u043D\u043E\u0432, \u0438 \u0443\u043F\u0435\u0440\u0435\u0442\u044C\u0441\u044F \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442 \u043D\u0430 \u0441\u0435\u0440\u0435\u0434\u0438\u043D\u0435 \u0445\u0443\u0436\u0435, \u0447\u0435\u043C \u0432\u0435\u0440\u043D\u0443\u0442\u044C \u0435\u0451 \u0441\u0440\u0430\u0437\u0443; \u0440\u0430\u0437\u0431\u0438\u0435\u043D\u0438\u0435 \u043C\u043E\u0436\u0435\u0448\u044C \u041F\u0420\u0415\u0414\u041B\u041E\u0416\u0418\u0422\u042C (split), \u0430 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438 \u043D\u0435 \u0437\u0430\u0432\u043E\u0434\u0438 \u2014 \u044D\u0442\u043E \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u0430\u0432\u0442\u043E\u0440\u0430. \u0417\u0430\u0434\u0430\u0447\u0430 \u0443\u0439\u0434\u0451\u0442 \u0430\u0432\u0442\u043E\u0440\u0443 (\u043D\u0430\u0437\u043D\u0430\u0447\u0438\u043C \u043D\u0430 \u043D\u0435\u0433\u043E \u0438 \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0451\u043C \u0432 \u0440\u0430\u0431\u043E\u0442\u0443), \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0443\u0435\u0434\u0443\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u043E\u0442 \u0442\u0432\u043E\u0435\u0433\u043E \u0438\u043C\u0435\u043D\u0438. \u041F\u043E\u0441\u043B\u0435 \u0432\u044B\u0437\u043E\u0432\u0430 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0437\u043E\u0432\u0438 \u2014 \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0441\u0435\u0441\u0441\u0438\u044E.",
|
|
11534
12050
|
{
|
|
12051
|
+
// TASWM-505: КОД причины рядом с прозой. Проза объясняет автору ЭТУ задачу, код
|
|
12052
|
+
// складывается по бэклогу в распределение «чего в постановках не хватает чаще
|
|
12053
|
+
// всего» (аналитика роя). Обязательный: необязательное поле дрон пропустит, а
|
|
12054
|
+
// распределение по половине возвратов — не распределение.
|
|
12055
|
+
reason: z.enum(RETURN_REASONS).describe(
|
|
12056
|
+
"\u043A\u043E\u0434 \u043F\u0440\u0438\u0447\u0438\u043D\u044B: unclear_goal \u2014 \u043D\u0435\u043F\u043E\u043D\u044F\u0442\u043D\u043E, \u0427\u0422\u041E \u0434\u0435\u043B\u0430\u0442\u044C; no_acceptance \u2014 \u043D\u0435\u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442; too_big \u2014 \u043F\u043E\u043D\u044F\u0442\u043D\u043E \u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u043E, \u043D\u043E \u044D\u0442\u043E \u043D\u0435 \u0437\u0430\u0434\u0430\u0447\u0430, \u0430 \u043F\u0430\u0447\u043A\u0430 \u0437\u0430\u0434\u0430\u0447 (\u043B\u0438\u0441\u0442, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0437\u0430 \u043E\u0434\u0438\u043D \u043F\u0440\u043E\u0433\u043E\u043D \u043D\u0435 \u0434\u043E\u0432\u0435\u0441\u0442\u0438); contradiction \u2014 \u043F\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0442 \u0441\u0430\u043C\u0430 \u0441\u0435\u0431\u0435 \u0438\u043B\u0438 \u0443\u0436\u0435 \u043F\u0440\u0438\u043D\u044F\u0442\u043E\u043C\u0443 \u0440\u0435\u0448\u0435\u043D\u0438\u044E"
|
|
12057
|
+
),
|
|
11535
12058
|
missing: z.string().min(30).describe("\u0447\u0435\u0433\u043E \u0432 \u0437\u0430\u0434\u0430\u0447\u0435 \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442, \u0447\u0442\u043E\u0431\u044B \u0435\u0451 \u0441\u0434\u0435\u043B\u0430\u0442\u044C \u0438\u043B\u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u2014 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E, \u0430 \u043D\u0435 \xAB\u043D\u0435\u043F\u043E\u043D\u044F\u0442\u043D\u043E\xBB"),
|
|
11536
12059
|
readings: z.array(z.string().min(10)).optional().describe("\u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u044F \u043F\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0442\u044B \u0440\u0430\u0441\u0441\u043C\u043E\u0442\u0440\u0435\u043B (\u0438 \u0447\u0435\u043C \u043A\u0430\u0436\u0434\u043E\u0435 \u043F\u043B\u043E\u0445\u043E/\u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0432\u043E)"),
|
|
11537
12060
|
assumed: z.string().min(30).describe("\u0447\u0442\u043E \u0442\u044B \u043F\u0440\u0435\u0434\u043F\u043E\u043B\u043E\u0436\u0438\u043B \u0431\u044B \u0441\u0434\u0435\u043B\u0430\u0442\u044C, \u0435\u0441\u043B\u0438 \u0431\u044B \u043F\u0440\u0438\u0448\u043B\u043E\u0441\u044C \u0433\u0430\u0434\u0430\u0442\u044C, \u0438 \u043F\u043E\u0447\u0435\u043C\u0443 \u043D\u0435 \u0441\u0442\u0430\u043B"),
|
|
11538
|
-
questions: z.array(z.string().min(10)).min(1).max(7).describe("\u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0430\u0432\u0442\u043E\u0440\u0443 \u2014 \u043E\u0442\u0432\u0435\u0442\u044B \u043D\u0430 \u043D\u0438\u0445 \u0434\u043E\u043B\u0436\u043D\u044B \u0434\u0435\u043B\u0430\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u043C\u043E\u0439")
|
|
12061
|
+
questions: z.array(z.string().min(10)).min(1).max(7).describe("\u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0430\u0432\u0442\u043E\u0440\u0443 \u2014 \u043E\u0442\u0432\u0435\u0442\u044B \u043D\u0430 \u043D\u0438\u0445 \u0434\u043E\u043B\u0436\u043D\u044B \u0434\u0435\u043B\u0430\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u043C\u043E\u0439"),
|
|
12062
|
+
// TASWM-505: разбиение — ПРЕДЛОЖЕНИЕ автору, а не действие дрона. Подзадачи дрон не
|
|
12063
|
+
// заводит сознательно: разбиение — продуктовое решение, а плохое разбиение хуже
|
|
12064
|
+
// никакого, потому что выглядит авторитетно (заведённые подзадачи прочтут как
|
|
12065
|
+
// согласованный план). Поэтому — строчками в комменте, которые автор правит или
|
|
12066
|
+
// выбрасывает одним движением.
|
|
12067
|
+
split: z.array(z.string().min(10)).max(7).optional().describe(
|
|
12068
|
+
"\u0422\u041E\u041B\u042C\u041A\u041E \u043A reason=too_big: \u043A\u0430\u043A \u0431\u044B \u0442\u044B \u0440\u0430\u0437\u0431\u0438\u043B \u0437\u0430\u0434\u0430\u0447\u0443 \u2014 \u043F\u043E \u043E\u0434\u043D\u043E\u043C\u0443 \u043A\u0443\u0441\u043A\u0443 \u043D\u0430 \u0441\u0442\u0440\u043E\u043A\u0443. \u0423\u0435\u0434\u0435\u0442 \u0430\u0432\u0442\u043E\u0440\u0443 \u041F\u0420\u0415\u0414\u041B\u041E\u0416\u0415\u041D\u0418\u0415\u041C \u0432 \u0442\u043E\u043C \u0436\u0435 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0435; \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438 \u0441\u0430\u043C \u041D\u0415 \u0437\u0430\u0432\u043E\u0434\u0438 (\u0440\u0430\u0437\u0431\u0438\u0435\u043D\u0438\u0435 \u2014 \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u0430\u0432\u0442\u043E\u0440\u0430)"
|
|
12069
|
+
)
|
|
11539
12070
|
},
|
|
11540
12071
|
guard(
|
|
11541
12072
|
async ({
|
|
12073
|
+
reason,
|
|
11542
12074
|
missing,
|
|
11543
12075
|
readings,
|
|
11544
12076
|
assumed,
|
|
11545
|
-
questions
|
|
12077
|
+
questions,
|
|
12078
|
+
split
|
|
11546
12079
|
}) => {
|
|
11547
12080
|
if (await overmindDead()) return freeze();
|
|
11548
12081
|
const run = await store.myRun();
|
|
@@ -11577,10 +12110,20 @@ ${notFoundText}` : notFoundText }],
|
|
|
11577
12110
|
"",
|
|
11578
12111
|
`${author ? `${author.name}, ` : ""}\u044F \u043D\u0435 \u0431\u0435\u0440\u0443\u0441\u044C \u0437\u0430 \u044D\u0442\u0443 \u0437\u0430\u0434\u0430\u0447\u0443: \u0435\u0451 \u043D\u0435\u043B\u044C\u0437\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C, \u043D\u0435 \u0432\u044B\u0434\u0443\u043C\u0430\u0432 \u043F\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0443 \u0437\u0430 \u0442\u0435\u0431\u044F. \u0427\u0442\u043E \u043C\u0435\u0448\u0430\u0435\u0442:`,
|
|
11579
12112
|
"",
|
|
12113
|
+
`**\u041F\u0440\u0438\u0447\u0438\u043D\u0430.** ${returnReasonLabel(reason)} (\`${reason}\`).`,
|
|
12114
|
+
"",
|
|
11580
12115
|
`**\u0427\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442.** ${missing.trim()}`,
|
|
11581
12116
|
...readings?.length ? ["", "**\u041A\u0430\u043A \u044D\u0442\u043E \u043C\u043E\u0436\u043D\u043E \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C.**", ...readings.map((r) => `- ${r.trim()}`)] : [],
|
|
11582
12117
|
"",
|
|
11583
12118
|
`**\u0427\u0442\u043E \u044F \u043F\u0440\u0435\u0434\u043F\u043E\u043B\u043E\u0436\u0438\u043B \u0431\u044B \u0438 \u043F\u043E\u0447\u0435\u043C\u0443 \u043D\u0435 \u0441\u0442\u0430\u043B.** ${assumed.trim()}`,
|
|
12119
|
+
// Разбиение — только предложение и только к too_big (TASWM-505). Формулировка
|
|
12120
|
+
// «на твоё усмотрение» здесь не вежливость: заведённые подзадачи выглядели бы
|
|
12121
|
+
// согласованным планом, а это решение автора, и плохое разбиение хуже никакого.
|
|
12122
|
+
...reason === "too_big" && split?.length ? [
|
|
12123
|
+
"",
|
|
12124
|
+
"**\u041A\u0430\u043A \u044D\u0442\u043E \u043C\u043E\u0436\u043D\u043E \u0440\u0430\u0437\u0431\u0438\u0442\u044C** \u2014 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435, \u043D\u0435 \u0440\u0430\u0437\u0431\u0438\u0435\u043D\u0438\u0435: \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438 \u044F \u043D\u0435 \u0437\u0430\u0432\u043E\u0434\u0438\u043B, \u0440\u0435\u0448\u0430\u0442\u044C \u0442\u0435\u0431\u0435.",
|
|
12125
|
+
...split.map((s) => `- ${s.trim()}`)
|
|
12126
|
+
] : [],
|
|
11584
12127
|
"",
|
|
11585
12128
|
"**\u0412\u043E\u043F\u0440\u043E\u0441\u044B, \u043F\u043E\u0441\u043B\u0435 \u043E\u0442\u0432\u0435\u0442\u043E\u0432 \u043D\u0430 \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0443 \u043C\u043E\u0436\u043D\u043E \u0431\u0440\u0430\u0442\u044C:**",
|
|
11586
12129
|
...questions.map((q, i) => `${i + 1}. ${q.trim()}`)
|
|
@@ -11624,9 +12167,9 @@ ${notFoundText}` : notFoundText }],
|
|
|
11624
12167
|
};
|
|
11625
12168
|
}
|
|
11626
12169
|
}
|
|
11627
|
-
const
|
|
11628
|
-
await store.updateRun(null, { status: "returned", stop_reason: reason });
|
|
11629
|
-
await store.addEvent(null, "returned", failures.length ? `${
|
|
12170
|
+
const stopReason = `\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443${author ? ` (${author.name})` : ""} \xB7 ${returnReasonLabel(reason)}: ${missing.trim()}`;
|
|
12171
|
+
await store.updateRun(null, { status: "returned", stop_reason: stopReason, return_reason: reason });
|
|
12172
|
+
await store.addEvent(null, "returned", failures.length ? `${stopReason} \xB7 \u043D\u0435 \u0434\u043E\u0435\u0445\u0430\u043B\u043E: ${failures.join("; ")}` : stopReason);
|
|
11630
12173
|
return {
|
|
11631
12174
|
content: [
|
|
11632
12175
|
{
|
|
@@ -11972,9 +12515,11 @@ ${report}${shots.warning}`,
|
|
|
11972
12515
|
const headBranch = opts.prHeadBranch ? await opts.prHeadBranch(pr_url).catch(() => null) : null;
|
|
11973
12516
|
const branchFix = branchFromPrPatch(run?.branch, headBranch);
|
|
11974
12517
|
if (branchFix) await store.addEvent(null, "step", branchFix.event);
|
|
12518
|
+
const held = await changedPaths(opts.worktree ?? process.cwd(), { baseBranch: run?.base_branch ?? null }).catch(() => []);
|
|
12519
|
+
const heldPatch = held.length ? { changed_paths: held } : {};
|
|
11975
12520
|
if (run) {
|
|
11976
12521
|
const t = reviewTransitionPatch(run, pr_url);
|
|
11977
|
-
await store.updateRun(null, { ...t.patch, ...branchFix?.patch ?? {} });
|
|
12522
|
+
await store.updateRun(null, { ...t.patch, ...branchFix?.patch ?? {}, ...heldPatch });
|
|
11978
12523
|
await store.addEvent(null, "pr_opened", pr_url);
|
|
11979
12524
|
await postReport(reportSlug(run.task_slug), t.cycle ?? run.review_cycle ?? 0);
|
|
11980
12525
|
const rulesNote = await offerRules(reportSlug(run.task_slug));
|
|
@@ -11989,7 +12534,7 @@ ${report}${shots.warning}`,
|
|
|
11989
12534
|
}
|
|
11990
12535
|
return { content: [{ type: "text", text: `\u043F\u0440\u0438\u043D\u044F\u0442\u043E, \u0443\u0448\u043B\u043E \u043D\u0430 \u0440\u0435\u0432\u044C\u044E${shotsNote(shots)}${rulesNote}${zoneNote}` }] };
|
|
11991
12536
|
}
|
|
11992
|
-
await store.updateRun(null, { status: "review", pr_url, ...branchFix?.patch ?? {} });
|
|
12537
|
+
await store.updateRun(null, { status: "review", pr_url, ...branchFix?.patch ?? {}, ...heldPatch });
|
|
11993
12538
|
await store.addEvent(null, "pr_opened", pr_url);
|
|
11994
12539
|
const fallbackSlug = reportSlug(void 0);
|
|
11995
12540
|
if (fallbackSlug) await postReport(fallbackSlug, void 0);
|
|
@@ -12062,8 +12607,9 @@ ${report}${shots.warning}`,
|
|
|
12062
12607
|
if (!taskless) return errText2("Taskless \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (slug-only \u0440\u0435\u0436\u0438\u043C) \u2014 \u0434\u0438\u0441\u043F\u0430\u0442\u0447 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D.");
|
|
12063
12608
|
const hero = await heroRun();
|
|
12064
12609
|
if (!hero) return errText2("\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0442\u0432\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u2014 \u0434\u0438\u0441\u043F\u0430\u0442\u0447 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D, \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \u043F\u043E\u0437\u0436\u0435.");
|
|
12065
|
-
const
|
|
12066
|
-
if (!
|
|
12610
|
+
const read = await taskless.getTaskDetailed(slug);
|
|
12611
|
+
if (!read.ok) return errText2(`\u043D\u0435 \u0441\u043C\u043E\u0433 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 ${slug}: ${describeToolFailure(read.failure)}.`);
|
|
12612
|
+
const task = read.task;
|
|
12067
12613
|
const ep = await epic(hero.task_slug);
|
|
12068
12614
|
if (!ep || task.parentId !== ep.id) {
|
|
12069
12615
|
return errText2(`${slug} \u2014 \u043D\u0435 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0430 \u0442\u0432\u043E\u0435\u0433\u043E \u044D\u043F\u0438\u043A\u0430 ${hero.task_slug}. \u0414\u0438\u0441\u043F\u0430\u0442\u0447\u0438\u0442\u044C \u043C\u043E\u0436\u043D\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u0432\u043E\u0438 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438.`);
|
|
@@ -12576,6 +13122,31 @@ function buildAnomalyCandidates(db, now, thresholds, opts) {
|
|
|
12576
13122
|
})
|
|
12577
13123
|
});
|
|
12578
13124
|
}
|
|
13125
|
+
const GROUP_DEP_UNCONFIRMED = {
|
|
13126
|
+
id: "dep_unconfirmed",
|
|
13127
|
+
title: (n) => `${n} ${plural2(n, ["\u043F\u0440\u043E\u0433\u043E\u043D \u0436\u0434\u0451\u0442", "\u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u0436\u0434\u0443\u0442", "\u043F\u0440\u043E\u0433\u043E\u043D\u043E\u0432 \u0436\u0434\u0443\u0442"])} \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u044C, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043F\u0440\u043E\u0434 \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0435\u0442`
|
|
13128
|
+
};
|
|
13129
|
+
for (const run of allRuns) {
|
|
13130
|
+
if (run.status !== "queued") continue;
|
|
13131
|
+
if (!run.depends_on || run.depends_on.length === 0) continue;
|
|
13132
|
+
const blind = db.depsUnconfirmed?.(run.depends_on) ?? [];
|
|
13133
|
+
if (!blind.length) continue;
|
|
13134
|
+
candidates.push({
|
|
13135
|
+
key: `dep_unconfirmed:${run.task_slug}`,
|
|
13136
|
+
// по СЛАГУ, а не по id зеркала (см. dep_missing)
|
|
13137
|
+
minDurationMs: thresholds.depMissingMs,
|
|
13138
|
+
group: GROUP_DEP_UNCONFIRMED,
|
|
13139
|
+
build: () => ({
|
|
13140
|
+
runId: run.id,
|
|
13141
|
+
// Пуш: сам рой из этого не выйдет — вердикт приходит только от прода, а прод
|
|
13142
|
+
// отказывает устойчиво. Лечит человек (обновить прод / перевыдать токен демона).
|
|
13143
|
+
level: "decision",
|
|
13144
|
+
title: runTitle(run, "\u0436\u0434\u0451\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043F\u0440\u043E\u0434 \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0435\u0442"),
|
|
13145
|
+
body: `${droneOf(run)}: \u043F\u0440\u043E\u0433\u043E\u043D \u0441\u0442\u043E\u0438\u0442 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u044C\u044E ${joinCapped(blind.map((d) => d.slug))}, \u043D\u043E \u043F\u0440\u043E\u0434 \u043D\u0430 swarm_deps_status \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 (${blind[0].attempts} \u043F\u043E\u043F\u044B\u0442(\u043E\u043A), \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u044F\u044F \u043E\u0448\u0438\u0431\u043A\u0430 \u2014 ${blind[0].error}). \u0412\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435 \u0431\u0443\u0434\u0435\u0442, \u043F\u043E\u043A\u0430 \u044D\u0442\u043E \u043D\u0435 \u043F\u043E\u0447\u0438\u043D\u0438\u0442\u044C: \u043F\u0440\u043E\u0433\u043E\u043D \u0441\u0430\u043C \u043D\u0435 \u043F\u043E\u0435\u0434\u0435\u0442, \u0430 \u043C\u0430\u0448\u0438\u043D\u0430 \u0441\u0432\u043E\u0431\u043E\u0434\u043D\u0430.`,
|
|
13146
|
+
...hrefOf(run.task_url)
|
|
13147
|
+
})
|
|
13148
|
+
});
|
|
13149
|
+
}
|
|
12579
13150
|
if (opts.gateReason && queuedCount > 0) {
|
|
12580
13151
|
const reason = opts.gateReason;
|
|
12581
13152
|
candidates.push({
|
|
@@ -12892,6 +13463,7 @@ function resilientSwarmMcp(opts = {}) {
|
|
|
12892
13463
|
|
|
12893
13464
|
// src/overmind.ts
|
|
12894
13465
|
init_taskless();
|
|
13466
|
+
init_toolFailure();
|
|
12895
13467
|
|
|
12896
13468
|
// src/workspaceAccess.ts
|
|
12897
13469
|
function workspaceAccessLines(input) {
|
|
@@ -13123,29 +13695,27 @@ function makeDroneCapReader(opts) {
|
|
|
13123
13695
|
|
|
13124
13696
|
// src/mergeGate.ts
|
|
13125
13697
|
init_src();
|
|
13698
|
+
init_config();
|
|
13126
13699
|
|
|
13127
13700
|
// src/mergeQueue.ts
|
|
13128
13701
|
init_src();
|
|
13702
|
+
init_config();
|
|
13129
13703
|
|
|
13130
13704
|
// src/prNumber.ts
|
|
13131
|
-
|
|
13132
|
-
const s = (ref || "").trim();
|
|
13133
|
-
if (!s) return null;
|
|
13134
|
-
const url = s.match(/\/pull\/(\d+)/);
|
|
13135
|
-
if (url) return Number(url[1]);
|
|
13136
|
-
const bare = s.match(/^#?(\d+)$/);
|
|
13137
|
-
return bare ? Number(bare[1]) : null;
|
|
13138
|
-
}
|
|
13705
|
+
init_src();
|
|
13139
13706
|
|
|
13140
13707
|
// src/mergeQueue.ts
|
|
13141
13708
|
var TERMINAL2 = /* @__PURE__ */ new Set(["done", "failed", "stalled", "stopped", "superseded", "returned"]);
|
|
13142
13709
|
function isMergeCandidate(r) {
|
|
13143
|
-
return !!r.pr_url && r
|
|
13710
|
+
return !!r.pr_url && isPrAuthorRun(r) && !TERMINAL2.has(r.status) && prNumberOf(r.pr_url) != null;
|
|
13711
|
+
}
|
|
13712
|
+
function isPrAuthorRun(r) {
|
|
13713
|
+
return r.reviews_run_id == null && r.verifies_run_id == null && r.resolves_run_id == null;
|
|
13144
13714
|
}
|
|
13145
|
-
function mergeQueueCandidates(runs, repo) {
|
|
13715
|
+
function mergeQueueCandidates(runs, repo, repoFallback) {
|
|
13146
13716
|
const byPr = /* @__PURE__ */ new Map();
|
|
13147
13717
|
for (const r of runs) {
|
|
13148
|
-
if (r.repo !== repo || !isMergeCandidate(r)) continue;
|
|
13718
|
+
if (effectiveRepoOf(r.repo, repoFallback) !== repo || !isMergeCandidate(r)) continue;
|
|
13149
13719
|
const num = prNumberOf(r.pr_url);
|
|
13150
13720
|
const seen = byPr.get(num);
|
|
13151
13721
|
if (!seen || r.id > seen.id) byPr.set(num, r);
|
|
@@ -13175,7 +13745,7 @@ function makeMergeQueueResolver(deps) {
|
|
|
13175
13745
|
if (!deps.baseSync || !run.pr_url) return null;
|
|
13176
13746
|
const num = prNumberOf(run.pr_url);
|
|
13177
13747
|
if (num == null) return null;
|
|
13178
|
-
const candidates = mergeQueueCandidates(deps.listRuns(), run.repo);
|
|
13748
|
+
const candidates = mergeQueueCandidates(deps.listRuns(), effectiveRepoOf(run.repo, deps.repoFallback), deps.repoFallback);
|
|
13179
13749
|
const mine = candidates.find((c) => prNumberOf(c.pr_url) === num);
|
|
13180
13750
|
if (!mine) return null;
|
|
13181
13751
|
const ready = [];
|
|
@@ -13209,15 +13779,16 @@ function needsBaseRefresh(d) {
|
|
|
13209
13779
|
}
|
|
13210
13780
|
|
|
13211
13781
|
// src/mergeGate.ts
|
|
13212
|
-
function findRunByPr(runs, ref) {
|
|
13213
|
-
const
|
|
13214
|
-
|
|
13215
|
-
|
|
13216
|
-
|
|
13217
|
-
|
|
13218
|
-
|
|
13219
|
-
|
|
13220
|
-
|
|
13782
|
+
function findRunByPr(runs, ref, scope = {}) {
|
|
13783
|
+
const rows = runs.map((run) => ({
|
|
13784
|
+
run,
|
|
13785
|
+
prUrl: run.pr_url,
|
|
13786
|
+
repoKey: scope.repoOfRun ? scope.repoOfRun(run) : null,
|
|
13787
|
+
author: isPrAuthorRun(run),
|
|
13788
|
+
reviewVerdict: run.review_verdict,
|
|
13789
|
+
order: run.id
|
|
13790
|
+
}));
|
|
13791
|
+
return pickRunForPr(rows, ref, scope.repo ?? null)?.run ?? null;
|
|
13221
13792
|
}
|
|
13222
13793
|
function makeMergeGateResolver(deps) {
|
|
13223
13794
|
return makeMergeGateParts(deps).gate;
|
|
@@ -13233,15 +13804,17 @@ function makeMergeGateParts(deps) {
|
|
|
13233
13804
|
verifyVerdict: run.verify_verdict ?? null
|
|
13234
13805
|
});
|
|
13235
13806
|
};
|
|
13807
|
+
const repoOfRun = deps.repoFullNameOf ? (r) => deps.repoFullNameOf(effectiveRepoOf(r.repo, deps.repoFallback)) : void 0;
|
|
13236
13808
|
const queueFor = makeMergeQueueResolver({
|
|
13237
13809
|
listRuns: deps.listRuns,
|
|
13238
13810
|
gateAllows: async (r) => (await gateFor(r)).allowed,
|
|
13811
|
+
...deps.repoFallback ? { repoFallback: deps.repoFallback } : {},
|
|
13239
13812
|
...deps.baseSync ? { baseSync: deps.baseSync } : {},
|
|
13240
13813
|
...deps.checks ? { checks: deps.checks } : {},
|
|
13241
13814
|
...deps.ttlMs != null ? { ttlMs: deps.ttlMs } : {}
|
|
13242
13815
|
});
|
|
13243
|
-
const gate = async (ref) => {
|
|
13244
|
-
const run = findRunByPr(deps.listRuns(), ref);
|
|
13816
|
+
const gate = async (ref, ctxRepo) => {
|
|
13817
|
+
const run = findRunByPr(deps.listRuns(), ref, { repo: ctxRepo ?? null, ...repoOfRun ? { repoOfRun } : {} });
|
|
13245
13818
|
if (!run) {
|
|
13246
13819
|
return { ...computeMergeDecision(computeMergeGate({ hasRun: false, autoReview: false, reviewVerdict: null }), null), run: null };
|
|
13247
13820
|
}
|
|
@@ -13264,22 +13837,22 @@ function makeMergeGateParts(deps) {
|
|
|
13264
13837
|
}
|
|
13265
13838
|
|
|
13266
13839
|
// src/envGate.ts
|
|
13267
|
-
import { existsSync as
|
|
13268
|
-
import { join as
|
|
13840
|
+
import { existsSync as existsSync15 } from "fs";
|
|
13841
|
+
import { join as join27 } from "path";
|
|
13269
13842
|
import { createConnection } from "net";
|
|
13270
13843
|
import { execa as execa2 } from "execa";
|
|
13271
13844
|
|
|
13272
13845
|
// src/platform.ts
|
|
13273
|
-
import { existsSync as
|
|
13274
|
-
import { delimiter, join as
|
|
13846
|
+
import { existsSync as existsSync14, readFileSync as readFileSync18 } from "fs";
|
|
13847
|
+
import { delimiter, join as join26 } from "path";
|
|
13275
13848
|
function resolveCommandPath(name, env = process.env, platform = process.platform) {
|
|
13276
|
-
if (name.includes("/") || name.includes("\\")) return
|
|
13849
|
+
if (name.includes("/") || name.includes("\\")) return existsSync14(name) ? name : null;
|
|
13277
13850
|
const dirs = (env.PATH ?? env.Path ?? "").split(delimiter).filter(Boolean);
|
|
13278
13851
|
const exts = platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean) : [""];
|
|
13279
13852
|
for (const dir of dirs) {
|
|
13280
13853
|
for (const ext of exts) {
|
|
13281
|
-
const candidate =
|
|
13282
|
-
if (
|
|
13854
|
+
const candidate = join26(dir, name + ext);
|
|
13855
|
+
if (existsSync14(candidate)) return candidate;
|
|
13283
13856
|
}
|
|
13284
13857
|
}
|
|
13285
13858
|
return null;
|
|
@@ -13362,8 +13935,8 @@ var COMPOSE_MARKERS = [
|
|
|
13362
13935
|
"compose.yaml"
|
|
13363
13936
|
];
|
|
13364
13937
|
function detectRequirements(repo, io = {}) {
|
|
13365
|
-
const exists = io.exists ??
|
|
13366
|
-
const marker = COMPOSE_MARKERS.find((f) => exists(
|
|
13938
|
+
const exists = io.exists ?? existsSync15;
|
|
13939
|
+
const marker = COMPOSE_MARKERS.find((f) => exists(join27(repo, f)));
|
|
13367
13940
|
return marker ? [{ kind: "docker", raw: `docker (\u0434\u0435\u0442\u0435\u043A\u0442: ${marker})` }] : [];
|
|
13368
13941
|
}
|
|
13369
13942
|
function resolveRequirements(repo, declared, io = {}) {
|
|
@@ -13498,210 +14071,10 @@ import { randomBytes as randomBytes3 } from "crypto";
|
|
|
13498
14071
|
import { basename as basename5 } from "path";
|
|
13499
14072
|
import { serve as serve2 } from "@hono/node-server";
|
|
13500
14073
|
|
|
13501
|
-
// src/diff.ts
|
|
13502
|
-
import { statSync as statSync10 } from "fs";
|
|
13503
|
-
import { join as join26 } from "path";
|
|
13504
|
-
import { $ as $4 } from "execa";
|
|
13505
|
-
var MAX_FILES = 200;
|
|
13506
|
-
var MAX_LINES_PER_FILE = 1500;
|
|
13507
|
-
var MAX_UNTRACKED = 60;
|
|
13508
|
-
function cleanPath(raw) {
|
|
13509
|
-
let p = raw.trim();
|
|
13510
|
-
if (p.startsWith('"') && p.endsWith('"') && p.length >= 2) p = p.slice(1, -1);
|
|
13511
|
-
return p;
|
|
13512
|
-
}
|
|
13513
|
-
function stripAB(raw) {
|
|
13514
|
-
const p = cleanPath(raw);
|
|
13515
|
-
if (p === "/dev/null") return p;
|
|
13516
|
-
if (p.startsWith("a/") || p.startsWith("b/")) return p.slice(2);
|
|
13517
|
-
return p;
|
|
13518
|
-
}
|
|
13519
|
-
function parseUnifiedDiff(text) {
|
|
13520
|
-
const files = [];
|
|
13521
|
-
if (!text) return files;
|
|
13522
|
-
const lines = text.split("\n");
|
|
13523
|
-
let f = null;
|
|
13524
|
-
let hunk = null;
|
|
13525
|
-
let oldNo = 0;
|
|
13526
|
-
let newNo = 0;
|
|
13527
|
-
const push = () => {
|
|
13528
|
-
if (f) files.push(f);
|
|
13529
|
-
};
|
|
13530
|
-
for (const line of lines) {
|
|
13531
|
-
if (line.startsWith("diff --git ")) {
|
|
13532
|
-
push();
|
|
13533
|
-
hunk = null;
|
|
13534
|
-
const m = line.match(/^diff --git (.+) (.+)$/);
|
|
13535
|
-
const guess = m ? stripAB(m[2]) : "";
|
|
13536
|
-
f = { path: guess, oldPath: null, status: "modified", added: 0, removed: 0, binary: false, hunks: [] };
|
|
13537
|
-
continue;
|
|
13538
|
-
}
|
|
13539
|
-
if (!f) continue;
|
|
13540
|
-
if (line.startsWith("new file mode")) {
|
|
13541
|
-
f.status = "added";
|
|
13542
|
-
continue;
|
|
13543
|
-
}
|
|
13544
|
-
if (line.startsWith("deleted file mode")) {
|
|
13545
|
-
f.status = "deleted";
|
|
13546
|
-
continue;
|
|
13547
|
-
}
|
|
13548
|
-
if (line.startsWith("rename from ") || line.startsWith("copy from ")) {
|
|
13549
|
-
f.oldPath = cleanPath(line.replace(/^(rename|copy) from /, ""));
|
|
13550
|
-
f.status = "renamed";
|
|
13551
|
-
continue;
|
|
13552
|
-
}
|
|
13553
|
-
if (line.startsWith("rename to ") || line.startsWith("copy to ")) {
|
|
13554
|
-
f.path = cleanPath(line.replace(/^(rename|copy) to /, ""));
|
|
13555
|
-
f.status = "renamed";
|
|
13556
|
-
continue;
|
|
13557
|
-
}
|
|
13558
|
-
if (line.startsWith("Binary files ")) {
|
|
13559
|
-
f.binary = true;
|
|
13560
|
-
continue;
|
|
13561
|
-
}
|
|
13562
|
-
if (line.startsWith("--- ")) {
|
|
13563
|
-
const p = stripAB(line.slice(4));
|
|
13564
|
-
if (p === "/dev/null") f.status = "added";
|
|
13565
|
-
continue;
|
|
13566
|
-
}
|
|
13567
|
-
if (line.startsWith("+++ ")) {
|
|
13568
|
-
const p = stripAB(line.slice(4));
|
|
13569
|
-
if (p === "/dev/null") f.status = "deleted";
|
|
13570
|
-
else if (p) f.path = p;
|
|
13571
|
-
continue;
|
|
13572
|
-
}
|
|
13573
|
-
if (line.startsWith("@@")) {
|
|
13574
|
-
const m = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@(.*)$/);
|
|
13575
|
-
if (m) {
|
|
13576
|
-
oldNo = Number(m[1]);
|
|
13577
|
-
newNo = Number(m[2]);
|
|
13578
|
-
hunk = { header: (m[3] ?? "").trim(), lines: [] };
|
|
13579
|
-
f.hunks.push(hunk);
|
|
13580
|
-
}
|
|
13581
|
-
continue;
|
|
13582
|
-
}
|
|
13583
|
-
if (!hunk) continue;
|
|
13584
|
-
if (line.startsWith("\\")) continue;
|
|
13585
|
-
const tag = line[0];
|
|
13586
|
-
const body = line.slice(1);
|
|
13587
|
-
if (tag === "+") {
|
|
13588
|
-
hunk.lines.push({ k: "add", o: null, n: newNo++, t: body });
|
|
13589
|
-
f.added++;
|
|
13590
|
-
} else if (tag === "-") {
|
|
13591
|
-
hunk.lines.push({ k: "del", o: oldNo++, n: null, t: body });
|
|
13592
|
-
f.removed++;
|
|
13593
|
-
} else if (tag === " ") {
|
|
13594
|
-
hunk.lines.push({ k: "ctx", o: oldNo++, n: newNo++, t: body });
|
|
13595
|
-
}
|
|
13596
|
-
}
|
|
13597
|
-
push();
|
|
13598
|
-
return files;
|
|
13599
|
-
}
|
|
13600
|
-
function truncateFile(f) {
|
|
13601
|
-
let count = 0;
|
|
13602
|
-
const hunks = [];
|
|
13603
|
-
let cut = false;
|
|
13604
|
-
for (const h of f.hunks) {
|
|
13605
|
-
if (count >= MAX_LINES_PER_FILE) {
|
|
13606
|
-
cut = true;
|
|
13607
|
-
break;
|
|
13608
|
-
}
|
|
13609
|
-
const room = MAX_LINES_PER_FILE - count;
|
|
13610
|
-
if (h.lines.length <= room) {
|
|
13611
|
-
hunks.push(h);
|
|
13612
|
-
count += h.lines.length;
|
|
13613
|
-
} else {
|
|
13614
|
-
hunks.push({ header: h.header, lines: h.lines.slice(0, room) });
|
|
13615
|
-
count += room;
|
|
13616
|
-
cut = true;
|
|
13617
|
-
break;
|
|
13618
|
-
}
|
|
13619
|
-
}
|
|
13620
|
-
return cut ? { ...f, hunks, truncated: true } : f;
|
|
13621
|
-
}
|
|
13622
|
-
async function resolveBase(wt, baseBranch) {
|
|
13623
|
-
const tryRef = async (ref) => {
|
|
13624
|
-
const sha = (await $4({ reject: false })`git -C ${wt} rev-parse --verify --quiet ${ref + "^{commit}"}`).stdout.trim();
|
|
13625
|
-
if (!sha) return null;
|
|
13626
|
-
const mb2 = (await $4({ reject: false })`git -C ${wt} merge-base HEAD ${ref}`).stdout.trim();
|
|
13627
|
-
return mb2 || null;
|
|
13628
|
-
};
|
|
13629
|
-
const originHead = (await $4({ reject: false })`git -C ${wt} symbolic-ref --quiet --short refs/remotes/origin/HEAD`).stdout.trim();
|
|
13630
|
-
const candidates = [
|
|
13631
|
-
...baseBranch ? [`origin/${baseBranch}`, baseBranch] : [],
|
|
13632
|
-
originHead,
|
|
13633
|
-
"origin/main",
|
|
13634
|
-
"origin/master",
|
|
13635
|
-
"main",
|
|
13636
|
-
"master"
|
|
13637
|
-
].filter(Boolean);
|
|
13638
|
-
for (const c of candidates) {
|
|
13639
|
-
const mb2 = await tryRef(c);
|
|
13640
|
-
if (mb2) return mb2;
|
|
13641
|
-
}
|
|
13642
|
-
return null;
|
|
13643
|
-
}
|
|
13644
|
-
function mtime(path) {
|
|
13645
|
-
try {
|
|
13646
|
-
return statSync10(path).mtimeMs;
|
|
13647
|
-
} catch {
|
|
13648
|
-
return 0;
|
|
13649
|
-
}
|
|
13650
|
-
}
|
|
13651
|
-
async function collectRunDiff(worktree, opts = {}) {
|
|
13652
|
-
const stub = (reason) => ({ ok: false, reason, pr: opts.pr ?? null, files: [], added: 0, removed: 0 });
|
|
13653
|
-
const gone = opts.status === "queued" ? "\u0434\u0440\u043E\u043D \u0435\u0449\u0451 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u2014 \u043A\u043E\u0434\u0430 \u043F\u043E\u043A\u0430 \u043D\u0435\u0442" : "worktree \u0443\u0431\u0440\u0430\u043D \u2014 \u043A\u043E\u0434 \u0441\u043C\u043E\u0442\u0440\u0438 \u0432 PR";
|
|
13654
|
-
if (!worktree) return stub(gone);
|
|
13655
|
-
const isWt = (await $4({ reject: false })`git -C ${worktree} rev-parse --is-inside-work-tree`).stdout.trim();
|
|
13656
|
-
if (isWt !== "true") return stub(gone);
|
|
13657
|
-
const base3 = await resolveBase(worktree, opts.baseBranch);
|
|
13658
|
-
const from = base3 ?? "HEAD";
|
|
13659
|
-
const trackedTxt = (await $4({ reject: false })`git -C ${worktree} diff --no-color --find-renames ${from} --`).stdout;
|
|
13660
|
-
let files = parseUnifiedDiff(trackedTxt);
|
|
13661
|
-
const untrackedRaw = (await $4({ reject: false })`git -C ${worktree} ls-files --others --exclude-standard -z`).stdout;
|
|
13662
|
-
const untracked = untrackedRaw.split("\0").filter(Boolean).slice(0, MAX_UNTRACKED);
|
|
13663
|
-
for (const rel of untracked) {
|
|
13664
|
-
const t = (await $4({ reject: false })`git -C ${worktree} diff --no-color --no-index -- /dev/null ${rel}`).stdout;
|
|
13665
|
-
for (const pf of parseUnifiedDiff(t)) {
|
|
13666
|
-
pf.status = "added";
|
|
13667
|
-
if (!pf.path || pf.path === "/dev/null") pf.path = rel;
|
|
13668
|
-
files.push(pf);
|
|
13669
|
-
}
|
|
13670
|
-
}
|
|
13671
|
-
files.sort((a, b) => mtime(join26(worktree, b.path)) - mtime(join26(worktree, a.path)));
|
|
13672
|
-
const truncatedFiles = files.length > MAX_FILES;
|
|
13673
|
-
if (truncatedFiles) files = files.slice(0, MAX_FILES);
|
|
13674
|
-
const added = files.reduce((s, f) => s + f.added, 0);
|
|
13675
|
-
const removed = files.reduce((s, f) => s + f.removed, 0);
|
|
13676
|
-
const out = files.map(truncateFile);
|
|
13677
|
-
return {
|
|
13678
|
-
ok: true,
|
|
13679
|
-
base: base3 ? base3.slice(0, 9) : null,
|
|
13680
|
-
files: out,
|
|
13681
|
-
added,
|
|
13682
|
-
removed,
|
|
13683
|
-
...truncatedFiles ? { truncated: true } : {}
|
|
13684
|
-
};
|
|
13685
|
-
}
|
|
13686
|
-
async function changedPaths(worktree, opts = {}) {
|
|
13687
|
-
if (!worktree) return [];
|
|
13688
|
-
try {
|
|
13689
|
-
const isWt = (await $4({ reject: false })`git -C ${worktree} rev-parse --is-inside-work-tree`).stdout.trim();
|
|
13690
|
-
if (isWt !== "true") return [];
|
|
13691
|
-
const from = await resolveBase(worktree, opts.baseBranch) ?? "HEAD";
|
|
13692
|
-
const tracked = (await $4({ reject: false })`git -C ${worktree} diff --name-only --find-renames ${from} --`).stdout;
|
|
13693
|
-
const untracked = (await $4({ reject: false })`git -C ${worktree} ls-files --others --exclude-standard -z`).stdout;
|
|
13694
|
-
const all = [...tracked.split("\n"), ...untracked.split("\0")].map((s) => s.trim()).filter(Boolean);
|
|
13695
|
-
return [...new Set(all)].sort();
|
|
13696
|
-
} catch {
|
|
13697
|
-
return [];
|
|
13698
|
-
}
|
|
13699
|
-
}
|
|
13700
|
-
|
|
13701
14074
|
// package.json
|
|
13702
14075
|
var package_default = {
|
|
13703
14076
|
name: "@taskless-app/swarm",
|
|
13704
|
-
version: "0.6.
|
|
14077
|
+
version: "0.6.26",
|
|
13705
14078
|
type: "module",
|
|
13706
14079
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
13707
14080
|
keywords: [
|
|
@@ -13731,6 +14104,7 @@ var package_default = {
|
|
|
13731
14104
|
files: [
|
|
13732
14105
|
"dist",
|
|
13733
14106
|
"docs/HERO.md",
|
|
14107
|
+
"hooks",
|
|
13734
14108
|
"roles"
|
|
13735
14109
|
],
|
|
13736
14110
|
scripts: {
|
|
@@ -13751,7 +14125,7 @@ var package_default = {
|
|
|
13751
14125
|
execa: "^10.0.0",
|
|
13752
14126
|
zod: "3.25.76"
|
|
13753
14127
|
},
|
|
13754
|
-
gitHead: "
|
|
14128
|
+
gitHead: "52416995d46b0a843374d14bfb00dd310f71c4bf"
|
|
13755
14129
|
};
|
|
13756
14130
|
|
|
13757
14131
|
// src/version.ts
|
|
@@ -15199,13 +15573,15 @@ function makeHandler(deps) {
|
|
|
15199
15573
|
if (p === "/merge-gate") {
|
|
15200
15574
|
const ref = url.searchParams.get("pr") ?? "";
|
|
15201
15575
|
if (!ref.trim()) return Response.json({ error: "\u043D\u0443\u0436\u0435\u043D \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 pr=<url|\u043D\u043E\u043C\u0435\u0440>" }, { status: 400 });
|
|
15576
|
+
const ctxRepo = url.searchParams.get("repo");
|
|
15202
15577
|
const resolve4 = deps.mergeGate ?? makeMergeGateResolver({
|
|
15203
15578
|
listRuns: () => deps.db.listRuns(),
|
|
15204
15579
|
fallbackAutoReview: SWARM_AUTO_REVIEW,
|
|
15205
|
-
autoVerify: SWARM_AUTO_VERIFY
|
|
15580
|
+
autoVerify: SWARM_AUTO_VERIFY,
|
|
15206
15581
|
// TASWM-344
|
|
15582
|
+
...defaultRepo ? { repoFallback: defaultRepo } : {}
|
|
15207
15583
|
});
|
|
15208
|
-
return Response.json(await resolve4(ref));
|
|
15584
|
+
return Response.json(await resolve4(ref, ctxRepo));
|
|
15209
15585
|
}
|
|
15210
15586
|
if (p === "/resources") return Response.json(hostResourceSnapshot());
|
|
15211
15587
|
if (p === "/timeline") return Response.json(timelineState(deps.db, Date.now(), readNarratorLog(deps.narratorLog)));
|
|
@@ -15384,21 +15760,21 @@ async function checkDaemonVersion(start, opts = {}) {
|
|
|
15384
15760
|
|
|
15385
15761
|
// src/hostCommands.ts
|
|
15386
15762
|
init_src();
|
|
15387
|
-
import { readFileSync as
|
|
15388
|
-
import { join as
|
|
15763
|
+
import { readFileSync as readFileSync19, renameSync as renameSync9, writeFileSync as writeFileSync14 } from "fs";
|
|
15764
|
+
import { join as join28 } from "path";
|
|
15389
15765
|
function hostCommandStatePath(home) {
|
|
15390
|
-
return
|
|
15766
|
+
return join28(home, "host-command.json");
|
|
15391
15767
|
}
|
|
15392
15768
|
function readState(path) {
|
|
15393
15769
|
try {
|
|
15394
|
-
return JSON.parse(
|
|
15770
|
+
return JSON.parse(readFileSync19(path, "utf8"));
|
|
15395
15771
|
} catch {
|
|
15396
15772
|
return {};
|
|
15397
15773
|
}
|
|
15398
15774
|
}
|
|
15399
15775
|
function writeState(path, state) {
|
|
15400
15776
|
try {
|
|
15401
|
-
|
|
15777
|
+
writeFileSync14(`${path}.tmp`, JSON.stringify(state));
|
|
15402
15778
|
renameSync9(`${path}.tmp`, path);
|
|
15403
15779
|
} catch {
|
|
15404
15780
|
}
|
|
@@ -15561,8 +15937,8 @@ async function doRestart(cmd, opts, note) {
|
|
|
15561
15937
|
|
|
15562
15938
|
// src/selfUpdate.ts
|
|
15563
15939
|
init_git();
|
|
15564
|
-
import { existsSync as
|
|
15565
|
-
import { join as
|
|
15940
|
+
import { existsSync as existsSync16 } from "fs";
|
|
15941
|
+
import { join as join29 } from "path";
|
|
15566
15942
|
import { $ as $5 } from "execa";
|
|
15567
15943
|
var DAEMON_PACKAGE = "@taskless-app/swarm";
|
|
15568
15944
|
var defaultRun = async (cmd, args, cwd) => {
|
|
@@ -15575,7 +15951,7 @@ function tail(text, max = 400) {
|
|
|
15575
15951
|
}
|
|
15576
15952
|
async function runSelfUpdate(io) {
|
|
15577
15953
|
const run = io.run ?? defaultRun;
|
|
15578
|
-
const exists = io.exists ??
|
|
15954
|
+
const exists = io.exists ?? existsSync16;
|
|
15579
15955
|
const repo = io.repo;
|
|
15580
15956
|
await io.onStep("fetch", "\u0442\u044F\u043D\u0443 \u0441\u0432\u0435\u0436\u0438\u0439 \u043A\u043E\u0434");
|
|
15581
15957
|
const fetched = await run("git", ["-C", repo, "fetch", "--quiet", "origin"], repo);
|
|
@@ -15604,7 +15980,7 @@ async function runSelfUpdate(io) {
|
|
|
15604
15980
|
};
|
|
15605
15981
|
}
|
|
15606
15982
|
await io.onStep("install", "\u0441\u0442\u0430\u0432\u043B\u044E \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438");
|
|
15607
|
-
if (!exists(
|
|
15983
|
+
if (!exists(join29(repo, "pnpm-lock.yaml"))) {
|
|
15608
15984
|
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" };
|
|
15609
15985
|
}
|
|
15610
15986
|
const installed2 = await run("pnpm", ["install", "--frozen-lockfile"], repo);
|
|
@@ -15763,19 +16139,19 @@ async function fetchExternalStorm(url, opts = {}) {
|
|
|
15763
16139
|
}
|
|
15764
16140
|
|
|
15765
16141
|
// src/anomalyState.ts
|
|
15766
|
-
import { existsSync as
|
|
15767
|
-
import { join as
|
|
16142
|
+
import { existsSync as existsSync17, readFileSync as readFileSync20, renameSync as renameSync10, writeFileSync as writeFileSync15 } from "fs";
|
|
16143
|
+
import { join as join30 } from "path";
|
|
15768
16144
|
var KEY_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
15769
16145
|
var MAX_RESOLVED = 200;
|
|
15770
16146
|
function emptyState() {
|
|
15771
16147
|
return { since: {}, lastFiredAt: {}, fires: {}, resolved: [], stopQuiet: null };
|
|
15772
16148
|
}
|
|
15773
16149
|
function anomalyStatePath(home) {
|
|
15774
|
-
return
|
|
16150
|
+
return join30(home, "anomaly-state.json");
|
|
15775
16151
|
}
|
|
15776
16152
|
function loadAnomalyState(home, now = Date.now(), io = {}) {
|
|
15777
|
-
const exists = io.exists ??
|
|
15778
|
-
const readFile2 = io.readFile ?? ((p) =>
|
|
16153
|
+
const exists = io.exists ?? existsSync17;
|
|
16154
|
+
const readFile2 = io.readFile ?? ((p) => readFileSync20(p, "utf8"));
|
|
15779
16155
|
const path = anomalyStatePath(home);
|
|
15780
16156
|
if (!exists(path)) return emptyState();
|
|
15781
16157
|
try {
|
|
@@ -15789,9 +16165,9 @@ function loadAnomalyState(home, now = Date.now(), io = {}) {
|
|
|
15789
16165
|
)
|
|
15790
16166
|
);
|
|
15791
16167
|
return {
|
|
15792
|
-
since:
|
|
15793
|
-
lastFiredAt:
|
|
15794
|
-
fires:
|
|
16168
|
+
since: pick3(since, fresh),
|
|
16169
|
+
lastFiredAt: pick3(lastFiredAt, fresh),
|
|
16170
|
+
fires: pick3(fires, fresh),
|
|
15795
16171
|
// TAS-485: очередь гашения НЕ фильтруем тем же TTL, что ключи. Она про
|
|
15796
16172
|
// уведомление, которое УЖЕ висит у человека в инбоксе: пролежав неделю
|
|
15797
16173
|
// недоставленным, гашение не становится лишним — оно становится нужнее.
|
|
@@ -15805,7 +16181,7 @@ function loadAnomalyState(home, now = Date.now(), io = {}) {
|
|
|
15805
16181
|
function saveAnomalyState(home, state, io = {}) {
|
|
15806
16182
|
const path = anomalyStatePath(home);
|
|
15807
16183
|
const writeFile3 = io.writeFile ?? ((p, data) => {
|
|
15808
|
-
|
|
16184
|
+
writeFileSync15(`${p}.tmp`, data);
|
|
15809
16185
|
renameSync10(`${p}.tmp`, p);
|
|
15810
16186
|
});
|
|
15811
16187
|
writeFile3(path, JSON.stringify(state, null, 2));
|
|
@@ -15875,7 +16251,7 @@ function strings(raw) {
|
|
|
15875
16251
|
if (!Array.isArray(raw)) return [];
|
|
15876
16252
|
return raw.filter((v) => typeof v === "string" && v.length > 0);
|
|
15877
16253
|
}
|
|
15878
|
-
function
|
|
16254
|
+
function pick3(src, keys) {
|
|
15879
16255
|
const out = {};
|
|
15880
16256
|
for (const k of keys) if (src[k] != null) out[k] = src[k];
|
|
15881
16257
|
return out;
|
|
@@ -16131,6 +16507,12 @@ function predictForRun(model, run) {
|
|
|
16131
16507
|
function short(files, n = 3) {
|
|
16132
16508
|
return files.slice(0, n).join(", ") + (files.length > n ? ` \u0438 \u0435\u0449\u0451 ${files.length - n}` : "");
|
|
16133
16509
|
}
|
|
16510
|
+
function heldBecause(w) {
|
|
16511
|
+
const parts = [];
|
|
16512
|
+
if (w.prUrl) parts.push(`\u0440\u0430\u0431\u043E\u0442\u0430 \u0441\u0434\u0430\u043D\u0430, PR \u0435\u0449\u0451 \u043D\u0435 \u0432\u043B\u0438\u0442: ${w.prUrl}`);
|
|
16513
|
+
if (!w.actual) parts.push(w.prUrl ? "\u0444\u0430\u0439\u043B\u044B \u0432\u0437\u044F\u0442\u044B \u043F\u043E \u043F\u0440\u0435\u0434\u0441\u043A\u0430\u0437\u0430\u043D\u0438\u044E \u2014 \u0441\u043D\u0438\u043C\u043A\u0430 \u043D\u0435 \u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C" : "\u043F\u043E \u043F\u0440\u0435\u0434\u0441\u043A\u0430\u0437\u0430\u043D\u0438\u044E \u2014 \u0434\u0440\u043E\u043D \u0435\u0449\u0451 \u043D\u0435 \u043D\u0430\u0447\u0430\u043B \u043F\u0438\u0441\u0430\u0442\u044C \u043A\u043E\u0434");
|
|
16514
|
+
return parts.length ? ` (${parts.join("; ")})` : "";
|
|
16515
|
+
}
|
|
16134
16516
|
var SPINE_PREDICTION_WEIGHT = 0.7;
|
|
16135
16517
|
function spineOfPrediction(spine, predicted) {
|
|
16136
16518
|
return predicted.filter((p) => spine.has(p.path) && p.weight >= SPINE_PREDICTION_WEIGHT).map((p) => p.path);
|
|
@@ -16151,7 +16533,7 @@ function decideConflict({ model, candidate, active }) {
|
|
|
16151
16533
|
return {
|
|
16152
16534
|
kind: "wait",
|
|
16153
16535
|
blockedBy: w.run,
|
|
16154
|
-
message: `\u044D\u0442\u0438 \u0434\u0432\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0442\u0440\u043E\u0433\u0430\u044E\u0442 \u043E\u0434\u043D\u0438 \u0444\u0430\u0439\u043B\u044B \u043E\u0431\u0449\u0435\u0433\u043E \u0445\u0440\u0435\u0431\u0442\u0430 (${short(shared)}) \u2014 \u0442\u0430\u043A\u0438\u0435 \u0438\u0434\u0443\u0442 \u043F\u043E\u0441\u043B\u0435\u0434\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u043D\u043E \u043D\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E \u043E\u0442 \u0437\u043E\u043D\u044B: \u0436\u0434\u0443 ${w.run.task_slug} (#${w.run.id})` + (w
|
|
16536
|
+
message: `\u044D\u0442\u0438 \u0434\u0432\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0442\u0440\u043E\u0433\u0430\u044E\u0442 \u043E\u0434\u043D\u0438 \u0444\u0430\u0439\u043B\u044B \u043E\u0431\u0449\u0435\u0433\u043E \u0445\u0440\u0435\u0431\u0442\u0430 (${short(shared)}) \u2014 \u0442\u0430\u043A\u0438\u0435 \u0438\u0434\u0443\u0442 \u043F\u043E\u0441\u043B\u0435\u0434\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u043D\u043E \u043D\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E \u043E\u0442 \u0437\u043E\u043D\u044B: \u0436\u0434\u0443 ${w.run.task_slug} (#${w.run.id})` + heldBecause(w)
|
|
16155
16537
|
};
|
|
16156
16538
|
}
|
|
16157
16539
|
}
|
|
@@ -16162,7 +16544,7 @@ function decideConflict({ model, candidate, active }) {
|
|
|
16162
16544
|
return {
|
|
16163
16545
|
kind: "wait",
|
|
16164
16546
|
blockedBy: w.run,
|
|
16165
|
-
message: `\u044D\u0442\u0438 \u0434\u0432\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0442\u0440\u043E\u0433\u0430\u044E\u0442 ${what} \u2014 \u0441\u0442\u0430\u0432\u0438\u043C \u043F\u043E\u0441\u043B\u0435\u0434\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u043D\u043E: \u0436\u0434\u0443 ${w.run.task_slug} (#${w.run.id})` + (w
|
|
16547
|
+
message: `\u044D\u0442\u0438 \u0434\u0432\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0442\u0440\u043E\u0433\u0430\u044E\u0442 ${what} \u2014 \u0441\u0442\u0430\u0432\u0438\u043C \u043F\u043E\u0441\u043B\u0435\u0434\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u043D\u043E: \u0436\u0434\u0443 ${w.run.task_slug} (#${w.run.id})` + heldBecause(w)
|
|
16166
16548
|
};
|
|
16167
16549
|
}
|
|
16168
16550
|
return { kind: "clear" };
|
|
@@ -16215,10 +16597,16 @@ var ActiveFilesCache = class {
|
|
|
16215
16597
|
this.cache.set(run.id, { at: now, files });
|
|
16216
16598
|
return files;
|
|
16217
16599
|
}
|
|
16218
|
-
/**
|
|
16219
|
-
|
|
16220
|
-
|
|
16221
|
-
|
|
16600
|
+
/**
|
|
16601
|
+
* Забыть всех, кто больше НЕ ДЕРЖИТ файлы. Раньше здесь стоял список ЖИВЫХ дронов и
|
|
16602
|
+
* комментарий «дрон закончил — его файлы больше не заняты»; это и была дырка TASWM-506:
|
|
16603
|
+
* дрон заканчивал, открывал PR, PR висел непомерженным — а строки уже считались
|
|
16604
|
+
* свободными. Держатель теперь переживает своего дрона и уходит из кэша на мерже
|
|
16605
|
+
* (прогон выходит из `review`), см. holdsFiles.
|
|
16606
|
+
*/
|
|
16607
|
+
prune(holderIds) {
|
|
16608
|
+
const holders = new Set(holderIds);
|
|
16609
|
+
for (const id of [...this.cache.keys()]) if (!holders.has(id)) this.cache.delete(id);
|
|
16222
16610
|
}
|
|
16223
16611
|
};
|
|
16224
16612
|
function withManualAreas(history, areas) {
|
|
@@ -16227,7 +16615,7 @@ function withManualAreas(history, areas) {
|
|
|
16227
16615
|
|
|
16228
16616
|
// src/overmind.ts
|
|
16229
16617
|
var ANOMALY_RESOLVE_BATCH = 50;
|
|
16230
|
-
var THIS_FILE =
|
|
16618
|
+
var THIS_FILE = fileURLToPath3(import.meta.url);
|
|
16231
16619
|
var executorHasSession = (p) => EXECUTOR.session.exists(p);
|
|
16232
16620
|
var OVERMIND_ENTRY = THIS_FILE;
|
|
16233
16621
|
var STALL_MS = Number(process.env.SWARM_STALL_MS) || 10 * 60 * 1e3;
|
|
@@ -16827,12 +17215,19 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
|
|
|
16827
17215
|
// вторую ветку мимо PR. Пишем ту, что реально записана на прогоне; пусто (первый спавн —
|
|
16828
17216
|
// ветку заводит spawnDrone из слага) — прежняя формулировка.
|
|
16829
17217
|
`\u0422\u044B \u2014 \u0434\u0440\u043E\u043D \u0440\u043E\u044F. \u0420\u0430\u0431\u043E\u0442\u0430\u0435\u0448\u044C \u0432 \u044D\u0442\u043E\u043C git-worktree, \u0432\u0435\u0442\u043A\u0430 \`${run.branch || run.task_slug}\`${run.branch && run.branch !== run.task_slug ? " (\u043E\u043D\u0430 \u041D\u0415 \u0440\u0430\u0432\u043D\u0430 \u0441\u043B\u0430\u0433\u0443 \u0437\u0430\u0434\u0430\u0447\u0438 \u2014 \u044D\u0442\u043E \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u0435 \u0443\u0436\u0435 \u043E\u0442\u043A\u0440\u044B\u0442\u043E\u0433\u043E PR, \u043F\u0443\u0448 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 \u043D\u0435\u0451)" : " \u0441\u043E\u0437\u0434\u0430\u043D\u0430 \u043F\u043E slug \u0437\u0430\u0434\u0430\u0447\u0438"}.`,
|
|
16830
|
-
"\u0418\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B overmind (MCP): report_step(text) \u2014 \u043B\u043E\u0433\u0438\u0440\u0443\u0439 \u0445\u043E\u0434 \u0440\u0430\u0431\u043E\u0442\u044B; read_task() \u2014 \u0441\u0432\u0435\u0436\u0438\u0439 \u0441\u043D\u0430\u043F\u0448\u043E\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 (\u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435+\u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438+\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u044B+\u0434\u043E\u043A\u0438+\u0438\u0441\u0445\u043E\u0434\u044B \u0441\u043E\u0441\u0435\u0434\u043D\u0438\u0445 \u0437\u0430\u0434\u0430\u0447 \u043F\u043E \u0437\u043E\u043D\u0435 \u043A\u043E\u0434\u0430) \u0431\u0435\u0437 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432, \u0435\u0441\u043B\u0438 \u0442\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u044F \u043C\u043E\u0433\u043B\u0438 \u0434\u043E\u043F\u0438\u0441\u0430\u0442\u044C \u043F\u043E\u0441\u043B\u0435 \u0441\u0442\u0430\u0440\u0442\u0430; ask(question, options?) \u2014 \u0435\u0441\u043B\u0438 \u0443\u043F\u0451\u0440\u0441\u044F \u0432 \u0420\u0415\u0428\u0415\u041D\u0418\u0415, \u0441\u043F\u0440\u043E\u0441\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 (\u0434\u0430\u0439 2\u20134 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u0430 \u0432 options, \u0435\u0441\u043B\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0430 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u0430\u044F) \u0438 \u0434\u043E\u0436\u0434\u0438\u0441\u044C \u043E\u0442\u0432\u0435\u0442\u0430 (\u041D\u0415 \u0443\u0433\u0430\u0434\u044B\u0432\u0430\u0439); report_acceptance_check(id, state, evidence) \u2014 \u0437\u0430\u043A\u0440\u043E\u0439 \u041A\u0410\u0416\u0414\u042B\u0419 \u043F\u0443\u043D\u043A\u0442 \u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0435\u0432 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 (id \u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 \u0431\u043B\u043E\u043A\u0430 \xAB\u041A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438\xBB, state = passed|failed|pending \u043F\u043E \u0444\u0430\u043A\u0442\u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0430, evidence \u2014 \u0432\u044B\u0432\u043E\u0434 \u043A\u043E\u043C\u0430\u043D\u0434\u044B/\u0441\u0441\u044B\u043B\u043A\u0430/\u0447\u0442\u043E \u043D\u0430\u0431\u043B\u044E\u0434\u0430\u043B); \u043F\u0440\u043E\u0439\u0434\u0438\u0441\u044C \u043F\u043E \u043D\u0438\u043C \u041F\u0415\u0420\u0415\u0414 done(), \u0438\u043D\u0430\u0447\u0435 \u0433\u0430\u043B\u043E\u0447\u043A\u0438 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u0440\u0443\u043A\u0430\u043C\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440; done(pr_url) \u2014 \u043A\u043E\u0433\u0434\u0430 \u043E\u0442\u043A\u0440\u043E\u0435\u0448\u044C PR; return_task(missing, readings, assumed, questions) \u2014 \u0432\u0435\u0440\u043D\u0443\u0442\u044C \u0421\u042B\u0420\u0423\u042E \u0437\u0430\u0434\u0430\u0447\u0443 \u0430\u0432\u0442\u043E\u0440\u0443 (\u0441\u043C. \u043F\u0440\u0430\u0432\u0438\u043B\u043E \u043D\u0438\u0436\u0435); note(text) \u2014 \u0437\u0430\u043C\u0435\u0442\u043A\u0430 \xAB\u0437\u0430\u043C\u0435\u0447\u0435\u043D\u043E \u043F\u043E \u043F\u0443\u0442\u0438\xBB \u0411\u0415\u0417 \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u043A\u0438: \u043D\u0430\u0442\u043A\u043D\u0443\u043B\u0441\u044F \u043D\u0430 \u043F\u0440\u043E\u0431\u0435\u043B \u0432 \u0434\u0438\u0437\u0430\u0439\u043D-\u0441\u0438\u0441\u0442\u0435\u043C\u0435, \u043F\u0440\u043E\u0442\u0443\u0445\u0448\u0443\u044E \u0438\u043D\u0441\u0442\u0440\u0443\u043A\u0446\u0438\u044E, \u0431\u0430\u0433 \u0440\u044F\u0434\u043E\u043C \u0441\u043E \u0441\u0432\u043E\u0438\u043C \u0434\u0438\u0444\u0444\u043E\u043C \u2014 \u0441\u043A\u0430\u0436\u0438 \u0438 \u0440\u0430\u0431\u043E\u0442\u0430\u0439 \u0434\u0430\u043B\u044C\u0448\u0435, \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u0443\u0432\u0438\u0434\u0438\u0442 \u0438 \u0441\u0430\u043C \u0440\u0435\u0448\u0438\u0442, \u0437\u0430\u0432\u043E\u0434\u0438\u0442\u044C \u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 (\u043D\u0435 \u043F\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0439 \u0442\u0430\u043A\u043E\u0435 \u0432 \u0434\u043E\u0440\u043E\u0433\u043E\u0439 \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u044E\u0449\u0438\u0439 ask \u0438 \u043D\u0435 \u0445\u043E\u0440\u043E\u043D\u0438 \u0432 \u043E\u0442\u0447\u0451\u0442\u0435 \u043E \u0441\u0434\u0430\u0447\u0435).",
|
|
17218
|
+
"\u0418\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B overmind (MCP): report_step(text) \u2014 \u043B\u043E\u0433\u0438\u0440\u0443\u0439 \u0445\u043E\u0434 \u0440\u0430\u0431\u043E\u0442\u044B; read_task() \u2014 \u0441\u0432\u0435\u0436\u0438\u0439 \u0441\u043D\u0430\u043F\u0448\u043E\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 (\u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435+\u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438+\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u044B+\u0434\u043E\u043A\u0438+\u0438\u0441\u0445\u043E\u0434\u044B \u0441\u043E\u0441\u0435\u0434\u043D\u0438\u0445 \u0437\u0430\u0434\u0430\u0447 \u043F\u043E \u0437\u043E\u043D\u0435 \u043A\u043E\u0434\u0430) \u0431\u0435\u0437 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432, \u0435\u0441\u043B\u0438 \u0442\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u044F \u043C\u043E\u0433\u043B\u0438 \u0434\u043E\u043F\u0438\u0441\u0430\u0442\u044C \u043F\u043E\u0441\u043B\u0435 \u0441\u0442\u0430\u0440\u0442\u0430; ask(question, options?) \u2014 \u0435\u0441\u043B\u0438 \u0443\u043F\u0451\u0440\u0441\u044F \u0432 \u0420\u0415\u0428\u0415\u041D\u0418\u0415, \u0441\u043F\u0440\u043E\u0441\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 (\u0434\u0430\u0439 2\u20134 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u0430 \u0432 options, \u0435\u0441\u043B\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0430 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u0430\u044F) \u0438 \u0434\u043E\u0436\u0434\u0438\u0441\u044C \u043E\u0442\u0432\u0435\u0442\u0430 (\u041D\u0415 \u0443\u0433\u0430\u0434\u044B\u0432\u0430\u0439); report_acceptance_check(id, state, evidence) \u2014 \u0437\u0430\u043A\u0440\u043E\u0439 \u041A\u0410\u0416\u0414\u042B\u0419 \u043F\u0443\u043D\u043A\u0442 \u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0435\u0432 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 (id \u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 \u0431\u043B\u043E\u043A\u0430 \xAB\u041A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438\xBB, state = passed|failed|pending \u043F\u043E \u0444\u0430\u043A\u0442\u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0430, evidence \u2014 \u0432\u044B\u0432\u043E\u0434 \u043A\u043E\u043C\u0430\u043D\u0434\u044B/\u0441\u0441\u044B\u043B\u043A\u0430/\u0447\u0442\u043E \u043D\u0430\u0431\u043B\u044E\u0434\u0430\u043B); \u043F\u0440\u043E\u0439\u0434\u0438\u0441\u044C \u043F\u043E \u043D\u0438\u043C \u041F\u0415\u0420\u0415\u0414 done(), \u0438\u043D\u0430\u0447\u0435 \u0433\u0430\u043B\u043E\u0447\u043A\u0438 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u0440\u0443\u043A\u0430\u043C\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440; done(pr_url) \u2014 \u043A\u043E\u0433\u0434\u0430 \u043E\u0442\u043A\u0440\u043E\u0435\u0448\u044C PR; return_task(reason, missing, readings, assumed, questions) \u2014 \u0432\u0435\u0440\u043D\u0443\u0442\u044C \u0421\u042B\u0420\u0423\u042E \u0437\u0430\u0434\u0430\u0447\u0443 \u0430\u0432\u0442\u043E\u0440\u0443 (\u0441\u043C. \u043F\u0440\u0430\u0432\u0438\u043B\u043E \u043D\u0438\u0436\u0435); note(text) \u2014 \u0437\u0430\u043C\u0435\u0442\u043A\u0430 \xAB\u0437\u0430\u043C\u0435\u0447\u0435\u043D\u043E \u043F\u043E \u043F\u0443\u0442\u0438\xBB \u0411\u0415\u0417 \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u043A\u0438: \u043D\u0430\u0442\u043A\u043D\u0443\u043B\u0441\u044F \u043D\u0430 \u043F\u0440\u043E\u0431\u0435\u043B \u0432 \u0434\u0438\u0437\u0430\u0439\u043D-\u0441\u0438\u0441\u0442\u0435\u043C\u0435, \u043F\u0440\u043E\u0442\u0443\u0445\u0448\u0443\u044E \u0438\u043D\u0441\u0442\u0440\u0443\u043A\u0446\u0438\u044E, \u0431\u0430\u0433 \u0440\u044F\u0434\u043E\u043C \u0441\u043E \u0441\u0432\u043E\u0438\u043C \u0434\u0438\u0444\u0444\u043E\u043C \u2014 \u0441\u043A\u0430\u0436\u0438 \u0438 \u0440\u0430\u0431\u043E\u0442\u0430\u0439 \u0434\u0430\u043B\u044C\u0448\u0435, \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u0443\u0432\u0438\u0434\u0438\u0442 \u0438 \u0441\u0430\u043C \u0440\u0435\u0448\u0438\u0442, \u0437\u0430\u0432\u043E\u0434\u0438\u0442\u044C \u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 (\u043D\u0435 \u043F\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0439 \u0442\u0430\u043A\u043E\u0435 \u0432 \u0434\u043E\u0440\u043E\u0433\u043E\u0439 \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u044E\u0449\u0438\u0439 ask \u0438 \u043D\u0435 \u0445\u043E\u0440\u043E\u043D\u0438 \u0432 \u043E\u0442\u0447\u0451\u0442\u0435 \u043E \u0441\u0434\u0430\u0447\u0435).",
|
|
16831
17219
|
// TASWM-412: третий выход из непонятной задачи. Раньше их было два, и оба плохие —
|
|
16832
17220
|
// угадать постановку (сделает не то) или ask (слот занят, а ответить может только автор
|
|
16833
17221
|
// задачи, не оператор роя). Граница здесь ОБЯЗАТЕЛЬНА: дрон, легко возвращающий задачи,
|
|
16834
17222
|
// становится отписочником, а это хуже, чем дрон, который иногда угадывает неверно.
|
|
16835
|
-
"\u0421\u042B\u0420\u0423\u042E \u0417\u0410\u0414\u0410\u0427\u0423 \u041D\u0415 \u0412\u042B\u0414\u0423\u041C\u042B\u0412\u0410\u0419 \u2014 \u0412\u0415\u0420\u041D\u0418 \u0410\u0412\u0422\u041E\u0420\u0423. \u041F\u0440\u043E\u0447\u0438\u0442\u0430\u0432 \u0437\u0430\u0434\u0430\u0447\u0443, \u043E\u0442\u0432\u0435\u0442\u044C \u0441\u0435\u0431\u0435 \u043D\u0430 \u0434\u0432\u0430 \u0432\u043E\u043F\u0440\u043E\u0441\u0430: \u043F\u043E\u043D\u044F\u0442\u043D\u043E \u043B\u0438, \u0427\u0422\u041E \u043D\u0443\u0436\u043D\u043E \u0441\u0434\u0435\u043B\u0430\u0442\u044C, \u0438 \u0435\u0441\u0442\u044C \u043B\u0438 \u0442\u043E, \u041F\u041E \u0427\u0415\u041C\u0423 \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442. \u041E\u0431\u0430 \xAB\u043D\u0435\u0442\xBB \u2192 return_task(missing, readings, assumed, questions): \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0443\u0435\u0434\u0443\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0430\u0432\u0442\u043E\u0440\u0443, \u0437\u0430\u0434\u0430\u0447\u0430 \u0443\u0439\u0434\u0451\u0442 \u043D\u0430 \u043D\u0435\u0433\u043E \u0438 \u0432 \u0440\u0430\u0431\u043E\u0442\u0443, \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u043E\u0435\u0442\u0441\u044F \u043A\u0430\u043A \xAB\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443\xBB (\u043D\u0435 \xAB\u043F\u0440\u043E\u0432\u0430\u043B\u0435\u043D\u0430\xBB), \u0441\u043B\u043E\u0442 \u043E\u0441\u0432\u043E\u0431\u043E\u0434\u0438\u0442\u0441\u044F. \u0412\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044C \u041C\u041E\u0416\u041D\u041E, \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435\u043B\u044C\u0437\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u043D\u0438 \u0432 \u043E\u0434\u043D\u043E\u043C \u0440\u0430\u0437\u0443\u043C\u043D\u043E\u043C \u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u0438 \u0438\u043B\u0438 \u043D\u0435\u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442. \u0422\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u0439 \u0434\u0432\u0430 \u0438 \u043E\u0431\u0430 \u043E\u0441\u043C\u044B\u0441\u043B\u0435\u043D\u043D\u044B \u2014 \u0432\u044B\u0431\u0435\u0440\u0438 \u043E\u0447\u0435\u0432\u0438\u0434\u043D\u043E\u0435, \u0441\u0434\u0435\u043B\u0430\u0439 \u0438 \u043D\u0430\u0437\u043E\u0432\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0443 \u0432 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0438 PR. \u0417\u0430\u0434\u0430\u0447\u0430 \u043F\u043E\u043D\u044F\u0442\u043D\u0430, \u043D\u043E \u0432 \u043E\u0434\u043D\u043E\u043C \u043F\u0443\u043D\u043A\u0442\u0435 \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0442 \u043F\u0440\u0430\u0432\u0438\u043B\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u2014 \u044D\u0442\u043E ask, \u0430 \u043D\u0435 \u0432\u043E\u0437\u0432\u0440\u0430\u0442.",
|
|
17223
|
+
"\u0421\u042B\u0420\u0423\u042E \u0417\u0410\u0414\u0410\u0427\u0423 \u041D\u0415 \u0412\u042B\u0414\u0423\u041C\u042B\u0412\u0410\u0419 \u2014 \u0412\u0415\u0420\u041D\u0418 \u0410\u0412\u0422\u041E\u0420\u0423. \u041F\u0440\u043E\u0447\u0438\u0442\u0430\u0432 \u0437\u0430\u0434\u0430\u0447\u0443, \u043E\u0442\u0432\u0435\u0442\u044C \u0441\u0435\u0431\u0435 \u043D\u0430 \u0434\u0432\u0430 \u0432\u043E\u043F\u0440\u043E\u0441\u0430: \u043F\u043E\u043D\u044F\u0442\u043D\u043E \u043B\u0438, \u0427\u0422\u041E \u043D\u0443\u0436\u043D\u043E \u0441\u0434\u0435\u043B\u0430\u0442\u044C, \u0438 \u0435\u0441\u0442\u044C \u043B\u0438 \u0442\u043E, \u041F\u041E \u0427\u0415\u041C\u0423 \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442. \u041E\u0431\u0430 \xAB\u043D\u0435\u0442\xBB \u2192 return_task(reason, missing, readings, assumed, questions): \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0443\u0435\u0434\u0443\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0430\u0432\u0442\u043E\u0440\u0443, \u0437\u0430\u0434\u0430\u0447\u0430 \u0443\u0439\u0434\u0451\u0442 \u043D\u0430 \u043D\u0435\u0433\u043E \u0438 \u0432 \u0440\u0430\u0431\u043E\u0442\u0443, \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u043E\u0435\u0442\u0441\u044F \u043A\u0430\u043A \xAB\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443\xBB (\u043D\u0435 \xAB\u043F\u0440\u043E\u0432\u0430\u043B\u0435\u043D\u0430\xBB), \u0441\u043B\u043E\u0442 \u043E\u0441\u0432\u043E\u0431\u043E\u0434\u0438\u0442\u0441\u044F. \u0412\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044C \u041C\u041E\u0416\u041D\u041E, \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435\u043B\u044C\u0437\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u043D\u0438 \u0432 \u043E\u0434\u043D\u043E\u043C \u0440\u0430\u0437\u0443\u043C\u043D\u043E\u043C \u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u0438 \u0438\u043B\u0438 \u043D\u0435\u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442. \u0422\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u0439 \u0434\u0432\u0430 \u0438 \u043E\u0431\u0430 \u043E\u0441\u043C\u044B\u0441\u043B\u0435\u043D\u043D\u044B \u2014 \u0432\u044B\u0431\u0435\u0440\u0438 \u043E\u0447\u0435\u0432\u0438\u0434\u043D\u043E\u0435, \u0441\u0434\u0435\u043B\u0430\u0439 \u0438 \u043D\u0430\u0437\u043E\u0432\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0443 \u0432 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0438 PR. \u0417\u0430\u0434\u0430\u0447\u0430 \u043F\u043E\u043D\u044F\u0442\u043D\u0430, \u043D\u043E \u0432 \u043E\u0434\u043D\u043E\u043C \u043F\u0443\u043D\u043A\u0442\u0435 \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0442 \u043F\u0440\u0430\u0432\u0438\u043B\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u2014 \u044D\u0442\u043E ask, \u0430 \u043D\u0435 \u0432\u043E\u0437\u0432\u0440\u0430\u0442. \u041F\u0440\u0438\u0447\u0438\u043D\u0443 \u043D\u0430\u0437\u043E\u0432\u0438 \u041A\u041E\u0414\u041E\u041C (reason): unclear_goal \u2014 \u043D\u0435\u043F\u043E\u043D\u044F\u0442\u043D\u043E, \u0447\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C; no_acceptance \u2014 \u043D\u0435\u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442; too_big \u2014 \u0441\u043C. \u043D\u0438\u0436\u0435; contradiction \u2014 \u043F\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0442 \u0441\u0430\u043C\u0430 \u0441\u0435\u0431\u0435. \u041A\u043E\u0434 \u043C\u0435\u0440\u044F\u0435\u0442 \u043D\u0435 \u0442\u0435\u0431\u044F, \u0430 \u043A\u0430\u0447\u0435\u0441\u0442\u0432\u043E \u043F\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043E\u043A \u043F\u043E \u0431\u044D\u043A\u043B\u043E\u0433\u0443, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u0441\u0442\u0430\u0432\u044C \u0447\u0435\u0441\u0442\u043D\u044B\u0439, \u0430 \u043D\u0435 \u0443\u0434\u043E\u0431\u043D\u044B\u0439.",
|
|
17224
|
+
// TASWM-505: ЕДИНСТВЕННЫЙ сорт негодной задачи, который не покрывался ничем. claimPolicy
|
|
17225
|
+
// (TASWM-357) отсеивает родителей с незакрытыми детьми, но лист на 8 часов уезжает дрону
|
|
17226
|
+
// целиком — и он упирается в потолок трат на середине, оставляя недоделанную работу
|
|
17227
|
+
// человеку. Это уже случалось. Подзадачи дрон при этом НЕ заводит: разбиение —
|
|
17228
|
+
// продуктовое решение, а плохое разбиение хуже никакого, потому что выглядит
|
|
17229
|
+
// авторитетно (заведённые подзадачи читаются как согласованный план).
|
|
17230
|
+
"\u0421\u041B\u0418\u0428\u041A\u041E\u041C \u0412\u0415\u041B\u0418\u041A\u0410 \u2014 \u0442\u043E\u0436\u0435 \u0432\u043E\u0437\u0432\u0440\u0430\u0442 (reason=too_big). \u0417\u0430\u0434\u0430\u0447\u0430 \u043F\u043E\u043D\u044F\u0442\u043D\u0430 \u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u0430, \u043D\u043E \u044D\u0442\u043E \u043D\u0435 \u0437\u0430\u0434\u0430\u0447\u0430, \u0430 \u043F\u0430\u0447\u043A\u0430 \u0437\u0430\u0434\u0430\u0447: \u0437\u0430 \u043E\u0434\u0438\u043D \u043F\u0440\u043E\u0433\u043E\u043D \u0435\u0451 \u043D\u0435 \u0434\u043E\u0432\u0435\u0441\u0442\u0438, \u0430 \u0443\u043F\u0435\u0440\u0435\u0442\u044C\u0441\u044F \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442 \u043D\u0430 \u0441\u0435\u0440\u0435\u0434\u0438\u043D\u0435 \u0445\u0443\u0436\u0435, \u0447\u0435\u043C \u0432\u0435\u0440\u043D\u0443\u0442\u044C \u0441\u0440\u0430\u0437\u0443 \u2014 \u043D\u0435\u0434\u043E\u0434\u0435\u043B\u0430\u043D\u043D\u043E\u0435 \u0434\u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443. \u041C\u0435\u0440\u0438\u043B\u043E \u043D\u0435 \xAB\u043C\u043D\u043E\u0433\u043E\u0432\u0430\u0442\u043E \u0440\u0430\u0431\u043E\u0442\u044B\xBB, \u0430 \xAB\u0432 \u043E\u0434\u043D\u043E\u043C \u043F\u0440\u043E\u0433\u043E\u043D\u0435 \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043A\u0438 \u043D\u0435 \u0437\u0430\u043A\u0440\u044B\u0442\u044C\xBB. \u0420\u0430\u0437\u0431\u0438\u0435\u043D\u0438\u0435 \u043C\u043E\u0436\u0435\u0448\u044C \u041F\u0420\u0415\u0414\u041B\u041E\u0416\u0418\u0422\u042C \u0430\u0432\u0442\u043E\u0440\u0443 \u0441\u043F\u0438\u0441\u043A\u043E\u043C (\u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 split \u0443 return_task) \u2014 \u043D\u043E \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438 \u041D\u0415 \u0437\u0430\u0432\u043E\u0434\u0438: \u0440\u0430\u0437\u0431\u0438\u0435\u043D\u0438\u0435 \u0440\u0435\u0448\u0430\u0435\u0442 \u0430\u0432\u0442\u043E\u0440.",
|
|
16836
17231
|
"\u041F\u0440\u0430\u0432\u0438\u043B\u0430: \u0442\u0440\u043E\u0433\u0430\u0435\u0448\u044C UI \u2014 \u0421\u041D\u0410\u0427\u0410\u041B\u0410 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0438, \u0435\u0441\u0442\u044C \u043B\u0438 \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0441\u043A\u0438\u043B\u043B \u0441 UI-\u043F\u0440\u0430\u0432\u0438\u043B\u0430\u043C\u0438 \u043F\u0440\u043E\u0435\u043A\u0442\u0430, \u0438 \u0441\u043B\u0435\u0434\u0443\u0439 \u0435\u0433\u043E \u0434\u0438\u0437\u0430\u0439\u043D-\u0441\u0438\u0441\u0442\u0435\u043C\u0435 (\u043D\u0435\u0442 \u043D\u0443\u0436\u043D\u043E\u0433\u043E \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u0430 \u2014 \u0437\u0430\u0432\u0435\u0434\u0438 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u0443\u044E \u0437\u0430\u0434\u0430\u0447\u0443, \u043D\u0435 \u0445\u0430\u0440\u0434\u043A\u043E\u0434\u044C \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u043E\u0435); \u043A\u043E\u043C\u043C\u0438\u0442\u044B \u0441\u043E slug \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0438; \u043F\u043E\u0441\u043B\u0435 UI-\u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439 \u043F\u0440\u0438\u043B\u043E\u0436\u0438 \u0441\u043A\u0440\u0438\u043D\u044B mobile+desktop \u041A \u0417\u0410\u0414\u0410\u0427\u0415 (\u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 screenshots \u0443 done); \u043E\u0442\u043A\u0440\u043E\u0439 PR \u0447\u0435\u0440\u0435\u0437 gh \u0438 \u0432\u044B\u0437\u043E\u0432\u0438 done(pr_url).",
|
|
16837
17232
|
// TASWM-377: у сессии есть ПОТОЛОК ТРАТ, и он режет её без предупреждения, прямо посреди
|
|
16838
17233
|
// работы. Дрон об этом знать обязан: цена сессии растёт от ЧИСЛА ХОДОВ (каждый ход заново
|
|
@@ -16890,10 +17285,10 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
|
|
|
16890
17285
|
}
|
|
16891
17286
|
return parts.join("\n");
|
|
16892
17287
|
}
|
|
16893
|
-
var HERO_CHARTER_PATH =
|
|
17288
|
+
var HERO_CHARTER_PATH = join31(dirname5(THIS_FILE), "..", "docs", "HERO.md");
|
|
16894
17289
|
function buildHeroPrompt(task, run, taskContext, attachmentsBlock) {
|
|
16895
17290
|
const name = run.name || `#${run.id}`;
|
|
16896
|
-
const charter =
|
|
17291
|
+
const charter = readFileSync21(HERO_CHARTER_PATH, "utf8");
|
|
16897
17292
|
const head = task ? `\u042D\u041F\u0418\u041A ${run.task_slug}: ${task.title}
|
|
16898
17293
|
|
|
16899
17294
|
${task.description}` : `\u042D\u041F\u0418\u041A ${run.task_slug} (\u0442\u0435\u043B\u043E \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E \u2014 \u0440\u0430\u0431\u043E\u0442\u0430\u0439 \u043F\u043E slug \u0438 \u0437\u0434\u0440\u0430\u0432\u043E\u043C\u0443 \u0441\u043C\u044B\u0441\u043B\u0443).`;
|
|
@@ -16936,7 +17331,7 @@ function buildResolverPrompt(reviewRun, resolver, conflicts, branch, mainRef) {
|
|
|
16936
17331
|
`1. \u0420\u0430\u0437\u0440\u0435\u0448\u0438 \u043C\u0430\u0440\u043A\u0435\u0440\u044B \u0432\u043E \u0432\u0441\u0435\u0445 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0443\u044E\u0449\u0438\u0445 \u0444\u0430\u0439\u043B\u0430\u0445 (${files}); \`git status\` \u043F\u043E\u043A\u0430\u0436\u0435\u0442, \u0435\u0441\u043B\u0438 \u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C.`,
|
|
16937
17332
|
"2. `git add -A && git commit --no-edit` \u2014 \u0437\u0430\u0432\u0435\u0440\u0448\u0438 merge-\u043A\u043E\u043C\u043C\u0438\u0442 (\u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E \u043E\u0441\u0442\u0430\u0432\u044C).",
|
|
16938
17333
|
"3. \u041F\u0440\u043E\u0433\u043E\u043D\u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u043A\u0430\u043A \u0432 CI (\u0437\u0430\u0433\u043B\u044F\u043D\u0438 \u0432 .github/workflows \u0438 package.json \u0440\u0435\u043F\u043E \u2014 \u0442\u0435\u0441\u0442\u044B/\u0442\u0438\u043F\u044B/\u0441\u0431\u043E\u0440\u043A\u0430/\u043B\u0438\u043D\u0442) \u0438 \u0443\u0431\u0435\u0434\u0438\u0441\u044C, \u0447\u0442\u043E \u0437\u0435\u043B\u0435\u043D\u043E. \u041A\u0440\u0430\u0441\u043D\u043E\u0435 \u2014 \u043F\u043E\u0447\u0438\u043D\u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u0435, \u043D\u0435 \u0441\u0430\u043C\u0443 \u0444\u0438\u0447\u0443.",
|
|
16939
|
-
`4. \`git push origin HEAD
|
|
17334
|
+
`4. \`git push origin HEAD:refs/heads/${branch}\` \u2014 \u041E\u0411\u042B\u0427\u041D\u042B\u0419 push (\u044D\u0442\u043E fast-forward), \u043F\u043E\u043B\u043D\u044B\u0439 refspec (\u043D\u0435 \u0433\u043E\u043B\u043E\u0435 \`HEAD:${branch}\` \u2014 git \u043E\u0442\u043A\u0430\u0436\u0435\u0442\u0441\u044F \u0443\u0433\u0430\u0434\u044B\u0432\u0430\u0442\u044C, \u0435\u0441\u043B\u0438 \u0432\u0435\u0442\u043A\u0443 \u0442\u0435\u043C \u0432\u0440\u0435\u043C\u0435\u043D\u0435\u043C \u0443\u0434\u0430\u043B\u0438\u043B\u0438). \`--force\`/\`--force-with-lease\` \u041D\u0415 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439: \u0440\u0438\u0431\u0435\u0439\u0437/\u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0438\u0441\u044C \u0438\u0441\u0442\u043E\u0440\u0438\u0438 \u0442\u0443\u0442 \u0437\u0430\u043F\u0440\u0435\u0449\u0435\u043D\u044B \u0438 \u0433\u0435\u0439\u0442\u044F\u0442\u0441\u044F.`,
|
|
16940
17335
|
`5. \u0412\u044B\u0437\u043E\u0432\u0438 done(${prUrl}) \u2014 \u0440\u043E\u0439 \u043F\u043E\u0434\u0445\u0432\u0430\u0442\u0438\u0442, \u0447\u0442\u043E \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D \u0438 PR \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D.`,
|
|
16941
17336
|
"",
|
|
16942
17337
|
`\u0412 \u043A\u0430\u0436\u0434\u044B\u0439 \u043A\u043E\u043C\u043C\u0438\u0442 \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u0439 \u0442\u0440\u0435\u0439\u043B\u0435\u0440-\u0441\u0442\u0440\u043E\u043A\u0443 \xABDrone: ${name} \xB7 resolver run #${resolver.id}\xBB. \u041A\u043E\u043C\u043C\u0438\u0442\u044C \u0438 \u043F\u0443\u0448\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 \u2014 \u043D\u0435\u0437\u0430\u043F\u0443\u0448\u0435\u043D\u043D\u043E\u0435 \u0443\u043C\u0440\u0451\u0442 \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u0434\u0440\u043E\u043D\u043E\u043C. \u0414\u043E\u043B\u0433\u043E\u0435 (\u0442\u0435\u0441\u0442\u044B) \u0434\u0435\u0440\u0436\u0438 \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u044E\u0449\u0438\u043C \u0432\u044B\u0437\u043E\u0432\u043E\u043C, \u043D\u0435 \u0432 \u0444\u043E\u043D\u0435.`
|
|
@@ -17197,7 +17592,7 @@ async function wakeRun(deps, runId) {
|
|
|
17197
17592
|
if (!run) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u0432 \u0437\u0435\u0440\u043A\u0430\u043B\u0435 \u0440\u043E\u044F` };
|
|
17198
17593
|
if (run.pid != null && deps.isAlive(run.pid)) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0443\u0436\u0435 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 (pid ${run.pid}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E` };
|
|
17199
17594
|
if (isTerminalStatus(run.status)) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435\u043D (${run.status}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E` };
|
|
17200
|
-
const worktreeExists = deps.worktreeExists ??
|
|
17595
|
+
const worktreeExists = deps.worktreeExists ?? existsSync18;
|
|
17201
17596
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
17202
17597
|
const wt = run.worktree_path;
|
|
17203
17598
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(wt);
|
|
@@ -17303,7 +17698,7 @@ function repoHasCi(deps, run) {
|
|
|
17303
17698
|
function repoHasCiWorkflows(worktreePath, repo) {
|
|
17304
17699
|
for (const base3 of [worktreePath, repo]) {
|
|
17305
17700
|
if (!base3) continue;
|
|
17306
|
-
const dir =
|
|
17701
|
+
const dir = join31(base3, ".github", "workflows");
|
|
17307
17702
|
try {
|
|
17308
17703
|
if (readdirSync13(dir).some((f) => f.endsWith(".yml") || f.endsWith(".yaml"))) return true;
|
|
17309
17704
|
} catch {
|
|
@@ -17489,7 +17884,7 @@ async function wakeReviewerOnCiReady(deps, run, over) {
|
|
|
17489
17884
|
return true;
|
|
17490
17885
|
}
|
|
17491
17886
|
function worktreeAndTranscriptAlive(deps, worktree) {
|
|
17492
|
-
const exists = deps.worktreeExists ??
|
|
17887
|
+
const exists = deps.worktreeExists ?? existsSync18;
|
|
17493
17888
|
const transcript = deps.hasTranscript ?? executorHasSession;
|
|
17494
17889
|
return exists(worktree) && transcript(worktree);
|
|
17495
17890
|
}
|
|
@@ -17523,7 +17918,9 @@ async function spawnFreshReviewerAfterStall(deps, run, why) {
|
|
|
17523
17918
|
async function wakeAuthorOnChanges(deps, run, now, droneCap) {
|
|
17524
17919
|
if (!deps.respawn) return false;
|
|
17525
17920
|
if (run.pid != null && deps.isAlive(run.pid)) return false;
|
|
17526
|
-
if (deps.db.activeReviewersFor(run.id).some((r) => r.status !== "stalled"))
|
|
17921
|
+
if (deps.db.activeReviewersFor(run.id).some((r) => r.status !== "stalled" && !isAwaitingCi(r.status, r.stop_reason))) {
|
|
17922
|
+
return false;
|
|
17923
|
+
}
|
|
17527
17924
|
if (deps.resourcesOk && !deps.resourcesOk()) return false;
|
|
17528
17925
|
if (countActiveDrones(deps.db) >= droneCap) return false;
|
|
17529
17926
|
if (!await projectQuotaGateAllows(deps, run)) return false;
|
|
@@ -17638,20 +18035,22 @@ async function envGateAllows(deps, run) {
|
|
|
17638
18035
|
return true;
|
|
17639
18036
|
}
|
|
17640
18037
|
function effectiveRepo(deps, run) {
|
|
17641
|
-
return
|
|
18038
|
+
return effectiveRepoOf(run.repo, deps.repoFallback);
|
|
17642
18039
|
}
|
|
17643
18040
|
async function zoneConflictFor(db, models, activeFiles, run, repo, repoFallback) {
|
|
17644
18041
|
const model = await models.forRepo(repo);
|
|
17645
18042
|
if (!hasZoneModel(model)) return { kind: "unknown" };
|
|
17646
|
-
const
|
|
17647
|
-
activeFiles.prune(
|
|
18043
|
+
const holders = db.listRuns().filter((r) => holdsFiles(r));
|
|
18044
|
+
activeFiles.prune(holders.map((r) => r.id));
|
|
17648
18045
|
const active = [];
|
|
17649
|
-
for (const r of
|
|
18046
|
+
for (const r of holders) {
|
|
17650
18047
|
if (r.id === run.id) continue;
|
|
17651
18048
|
if (normalizeRepo(r.repo || repoFallback || "") !== repo) continue;
|
|
17652
|
-
const
|
|
18049
|
+
const snapshot = r.changed_paths ?? [];
|
|
18050
|
+
const files = snapshot.length ? snapshot : await activeFiles.filesOf(r);
|
|
18051
|
+
const prUrl = r.status === "review" ? r.pr_url : null;
|
|
17653
18052
|
active.push(
|
|
17654
|
-
files.length ? { run: r, files, actual: true } : { run: r, files: predictForRun(model, r).map((p) => p.path), actual: false }
|
|
18053
|
+
files.length ? { run: r, files, actual: true, prUrl } : { run: r, files: predictForRun(model, r).map((p) => p.path), actual: false, prUrl }
|
|
17655
18054
|
);
|
|
17656
18055
|
}
|
|
17657
18056
|
return decideConflict({ model, candidate: run, active });
|
|
@@ -17797,7 +18196,8 @@ async function trySpawnQueuedRun(deps, run) {
|
|
|
17797
18196
|
}
|
|
17798
18197
|
const spentAttempts = preStartAttempts(deps.db.listRuns(), run.task_slug);
|
|
17799
18198
|
if (spentAttempts >= PRE_START_ATTEMPT_LIMIT) {
|
|
17800
|
-
const
|
|
18199
|
+
const last = lastPreStartAttempt(deps.db.listRuns(), run.task_slug);
|
|
18200
|
+
const why = last && isUnretriableParked(last) ? `${SPAWN_PARK_MARK}: \u043F\u0440\u0438\u0447\u0438\u043D\u0430 \u043E\u0442\u043A\u0430\u0437\u0430 \u043F\u043E\u0432\u0442\u043E\u0440\u043E\u043C \u043D\u0435 \u043B\u0435\u0447\u0438\u0442\u0441\u044F, \u043D\u043E\u0432\u043E\u0433\u043E \u0437\u0430\u0445\u043E\u0434\u0430 \u043D\u0435 \u0434\u0435\u043B\u0430\u044E (TASWM-485). \u0427\u0435\u043C \u043A\u043E\u043D\u0447\u0438\u043B\u043E\u0441\u044C \u2014 \u0432 \u043F\u0440\u043E\u0433\u043E\u043D\u0435 #${last.id}` : `${PRE_START_MARK} ${spentAttempts} \u0440\u0430\u0437\u0430 \u043F\u043E\u0434\u0440\u044F\u0434 \u2014 ${SPAWN_PARK_MARK}, \u0447\u0435\u0442\u0432\u0451\u0440\u0442\u043E\u0433\u043E \u0437\u0430\u0445\u043E\u0434\u0430 \u043D\u0435 \u0434\u0435\u043B\u0430\u044E (TASWM-422)`;
|
|
17801
18201
|
deps.db.updateRun(run.id, { status: "stopped", stop_reason: why });
|
|
17802
18202
|
deps.db.addEvent(run.id, "step", why);
|
|
17803
18203
|
const d = noteDefer(deps.spawnDefer ??= /* @__PURE__ */ new Map(), `pre-start-exhausted:${run.task_slug}`, why, GATE_LOG_HEARTBEAT_TICKS);
|
|
@@ -17816,7 +18216,8 @@ async function trySpawnQueuedRun(deps, run) {
|
|
|
17816
18216
|
const unmet = depVerdicts.filter((d) => !d.met);
|
|
17817
18217
|
if (unmet.length) {
|
|
17818
18218
|
const lost = unmet.filter((d) => d.outcome === "unknown");
|
|
17819
|
-
const
|
|
18219
|
+
const blind = deps.db.depsUnconfirmed?.(run.depends_on) ?? [];
|
|
18220
|
+
const why = lost.length ? `\u0436\u0434\u0451\u0442 \u0431\u043B\u043E\u043A\u0435\u0440\u0430, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u041D\u0415\u0422: ${lost.map((d) => d.slug).join(", ")} \u2014 \u043F\u043E \u044D\u0442\u043E\u0439 \u0441\u0441\u044B\u043B\u043A\u0435 \u043D\u0435\u0442 \u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u043F\u0440\u043E\u0433\u043E\u043D\u0430; \u0441\u0430\u043C \u043D\u0435 \u043F\u043E\u0435\u0434\u0435\u0442, \u0441\u043D\u0438\u043C\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u044C (swarm_run_upsert{dependsOn:[]}) \u0438\u043B\u0438 \u0437\u0430\u0432\u0435\u0434\u0438 \u0431\u043B\u043E\u043A\u0435\u0440 \u0437\u0430\u043D\u043E\u0432\u043E (TASWM-399)` : blind.length ? `\u0436\u0434\u0451\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438 ${blind.map((d) => d.slug).join(", ")}, \u043D\u043E \u041F\u0420\u041E\u0414 \u0415\u0401 \u041D\u0415 \u041F\u041E\u0414\u0422\u0412\u0415\u0420\u0416\u0414\u0410\u0415\u0422: ${blind[0].attempts} \u043F\u043E\u043F\u044B\u0442(\u043E\u043A) swarm_deps_status, \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u044F\u044F \u043E\u0448\u0438\u0431\u043A\u0430 \u2014 ${blind[0].error}. \u041F\u043E\u043A\u0430 \u043F\u0440\u043E\u0434 \u043C\u043E\u043B\u0447\u0438\u0442, \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438 \u043F\u0440\u043E\u0433\u043E\u043D \u0441\u0430\u043C \u043D\u0435 \u043F\u043E\u0435\u0434\u0435\u0442 (TASWM-486)` : `\u0436\u0434\u0451\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438: ${unmet.map((d) => d.slug).join(", ")}`;
|
|
17820
18221
|
return deferSpawn(deps, run, why);
|
|
17821
18222
|
}
|
|
17822
18223
|
if (!await envGateAllows(deps, run)) return "skipped";
|
|
@@ -17841,6 +18242,13 @@ async function trySpawnQueuedRun(deps, run) {
|
|
|
17841
18242
|
if (logged.get(run.id) !== e.message) {
|
|
17842
18243
|
logged.set(run.id, e.message);
|
|
17843
18244
|
deps.db.addEvent(run.id, "step", `${e.message} \u2014 \u0437\u0430\u0434\u0430\u0447\u0430 \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 (TASWM-348)`);
|
|
18245
|
+
const blocker = deps.db.getRun(e.blockedByRunId);
|
|
18246
|
+
if (blocker)
|
|
18247
|
+
deps.db.addEvent(
|
|
18248
|
+
blocker.id,
|
|
18249
|
+
"step",
|
|
18250
|
+
`${run.task_slug} (#${run.id}) \u0441\u0442\u043E\u0438\u0442 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u0438\u0437-\u0437\u0430 \u044D\u0442\u043E\u0439 \u0440\u0430\u0431\u043E\u0442\u044B \u2014 ${e.message} (TASWM-506)`
|
|
18251
|
+
);
|
|
17844
18252
|
narrate(deps, `${droneLabel(run)} \u2014 ${e.message}. \u041D\u0435 \u0441\u043F\u0430\u0432\u043D\u044E \u043F\u0430\u0440\u0430\u043B\u043B\u0435\u043B\u044C\u043D\u043E.`);
|
|
17845
18253
|
}
|
|
17846
18254
|
return "skipped";
|
|
@@ -17853,10 +18261,10 @@ async function trySpawnQueuedRun(deps, run) {
|
|
|
17853
18261
|
narrate(deps, `\u0411\u0435\u0440\u0443 ${run.task_slug} \u0438\u0437 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u2014 \u0441\u043F\u0430\u0432\u043D\u044E ${droneLabel(run)} (\u043F\u0440\u0438\u0431\u0440\u0430\u043B \u0437\u0430 \u043C\u0451\u0440\u0442\u0432\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0435\u0439).`);
|
|
17854
18262
|
return "spawned";
|
|
17855
18263
|
} catch (again) {
|
|
17856
|
-
return await failBeforeStart(deps, run, String(again));
|
|
18264
|
+
return await failBeforeStart(deps, run, String(again), spawnRetryOpts(again));
|
|
17857
18265
|
}
|
|
17858
18266
|
}
|
|
17859
|
-
return await failBeforeStart(deps, run, String(e));
|
|
18267
|
+
return await failBeforeStart(deps, run, String(e), spawnRetryOpts(e));
|
|
17860
18268
|
}
|
|
17861
18269
|
}
|
|
17862
18270
|
function holderStillHolds(deps, holder, now) {
|
|
@@ -17887,15 +18295,19 @@ async function healBusyWorktree(deps, run, e) {
|
|
|
17887
18295
|
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}`);
|
|
17888
18296
|
return true;
|
|
17889
18297
|
}
|
|
17890
|
-
|
|
18298
|
+
function spawnRetryOpts(e) {
|
|
18299
|
+
if (!(e instanceof TaskBodyUnavailableError)) return {};
|
|
18300
|
+
return { retriable: TOOL_FAILURE_RETRIABLE[e.code] };
|
|
18301
|
+
}
|
|
18302
|
+
async function failBeforeStart(deps, run, reason, opts = {}) {
|
|
17891
18303
|
deps.db.updateRun(run.id, { status: "failed", stop_reason: `${PRE_START_MARK}: ${reason}` });
|
|
17892
18304
|
deps.db.addEvent(run.id, "failed", `${PRE_START_MARK} \u2014 ${reason}`);
|
|
17893
18305
|
const attempts = preStartAttempts(deps.db.listRuns(), run.task_slug);
|
|
17894
|
-
if (attempts < PRE_START_ATTEMPT_LIMIT) {
|
|
18306
|
+
if (opts.retriable !== false && attempts < PRE_START_ATTEMPT_LIMIT) {
|
|
17895
18307
|
narrate(deps, `${droneLabel(run)} \u2014 ${PRE_START_MARK} (\u043F\u043E\u043F\u044B\u0442\u043A\u0430 ${attempts} \u0438\u0437 ${PRE_START_ATTEMPT_LIMIT}): ${reason}`);
|
|
17896
18308
|
return "failed";
|
|
17897
18309
|
}
|
|
17898
|
-
const parked = parkReason(attempts, reason);
|
|
18310
|
+
const parked = opts.retriable === false ? parkReasonUnretriable(reason) : parkReason(attempts, reason);
|
|
17899
18311
|
deps.db.updateRun(run.id, { stop_reason: parked });
|
|
17900
18312
|
deps.db.addEvent(run.id, "step", parked);
|
|
17901
18313
|
narrate(deps, `${run.task_slug}: ${parked}`);
|
|
@@ -17931,7 +18343,7 @@ async function resumeWatchdogParked(deps, droneCap) {
|
|
|
17931
18343
|
}
|
|
17932
18344
|
if (!candidate) return;
|
|
17933
18345
|
if (candidate.pid != null && deps.isAlive(candidate.pid)) return;
|
|
17934
|
-
const worktreeExists = deps.worktreeExists ??
|
|
18346
|
+
const worktreeExists = deps.worktreeExists ?? existsSync18;
|
|
17935
18347
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
17936
18348
|
const wt = candidate.worktree_path;
|
|
17937
18349
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(wt);
|
|
@@ -18100,7 +18512,7 @@ async function manageRelief(deps, now, droneCap, stormActive) {
|
|
|
18100
18512
|
if (resumeCandidate.pid != null && deps.isAlive(resumeCandidate.pid)) return;
|
|
18101
18513
|
if (!deps.respawn) return;
|
|
18102
18514
|
const wt = resumeCandidate.worktree_path;
|
|
18103
|
-
const worktreeExists = deps.worktreeExists ??
|
|
18515
|
+
const worktreeExists = deps.worktreeExists ?? existsSync18;
|
|
18104
18516
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
18105
18517
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(wt);
|
|
18106
18518
|
const mode = canContinue ? "continue" : "fresh";
|
|
@@ -18378,7 +18790,7 @@ function buildEscalationCandidates(db) {
|
|
|
18378
18790
|
}
|
|
18379
18791
|
async function tickOnce(deps) {
|
|
18380
18792
|
const now = deps.now ? deps.now() : Date.now();
|
|
18381
|
-
const worktreeExists = deps.worktreeExists ??
|
|
18793
|
+
const worktreeExists = deps.worktreeExists ?? existsSync18;
|
|
18382
18794
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
18383
18795
|
deps.sampleHost?.();
|
|
18384
18796
|
if (deps.rehydrate) {
|
|
@@ -18632,7 +19044,7 @@ async function tickOnce(deps) {
|
|
|
18632
19044
|
} else if (!st.conflicting && deps.ci && deps.respawn && !stormActive) {
|
|
18633
19045
|
await handleCi(deps, run, now, worktreeExists, hasTranscript, droneCap);
|
|
18634
19046
|
}
|
|
18635
|
-
if ((run
|
|
19047
|
+
if (needsFixIteration(run) && !stormActive && !st.conflicting && !deps.db.activeResolverFor(run.id) && await wakeAuthorOnChanges(deps, run, now, droneCap)) {
|
|
18636
19048
|
continue;
|
|
18637
19049
|
}
|
|
18638
19050
|
const reviewerGatesOk = !st.conflicting && !st.isDraft && !!deps.spawnReviewer && !stormActive && (!deps.reviewCutoffAt || run.updated_at >= deps.reviewCutoffAt) && deps.db.reviewerCountFor(run.id) === 0 && // ГЕЙТ ПЕРВЫМ: живой/успешный ревьюер уже есть — ни капа, ни спавна не трогаем
|
|
@@ -19140,7 +19552,7 @@ async function resolveConflict(deps, reviewRun) {
|
|
|
19140
19552
|
model: reviewRun.model,
|
|
19141
19553
|
review_model: reviewRun.review_model
|
|
19142
19554
|
});
|
|
19143
|
-
const wt =
|
|
19555
|
+
const wt = join31(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-resolve-${resolver.id}`);
|
|
19144
19556
|
try {
|
|
19145
19557
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
19146
19558
|
await deps.git.setWorktreeIdentity(wt, resolver.name);
|
|
@@ -19175,6 +19587,28 @@ async function resolveConflict(deps, reviewRun) {
|
|
|
19175
19587
|
throw e;
|
|
19176
19588
|
}
|
|
19177
19589
|
}
|
|
19590
|
+
function makeCheckerAttachmentsReader(tl) {
|
|
19591
|
+
return async (slug, worktree, workspaceId, onEvent) => {
|
|
19592
|
+
const client = tl.forWorkspace(workspaceId);
|
|
19593
|
+
const read = await client.getTaskDetailed(slug);
|
|
19594
|
+
if (!read.ok) {
|
|
19595
|
+
const why = `\u0432\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u0447\u0438 ${slug} \u043D\u0435 \u0447\u0438\u0442\u0430\u044E\u0442\u0441\u044F: ${describeToolFailure(read.failure)}`;
|
|
19596
|
+
onEvent?.(`${why} \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0438\u0434\u0451\u0442 \u0411\u0415\u0417 \u043C\u0430\u043A\u0435\u0442\u043E\u0432 \u0438 \u0444\u0430\u0439\u043B\u043E\u0432 \u0437\u0430\u0434\u0430\u0447\u0438`);
|
|
19597
|
+
return [
|
|
19598
|
+
`\u26A0\uFE0F \u0412\u041B\u041E\u0416\u0415\u041D\u0418\u042F \u0417\u0410\u0414\u0410\u0427\u0418 \u041D\u0415\u0414\u041E\u0421\u0422\u0423\u041F\u041D\u042B: ${why}.`,
|
|
19599
|
+
"\u041C\u0430\u043A\u0435\u0442\u044B \u0438 \u0444\u0430\u0439\u043B\u044B \u0437\u0430\u0434\u0430\u0447\u0438 \u0442\u044B \u041D\u0415 \u0432\u0438\u0434\u0435\u043B \u2014 \u0438\u0445 \u043C\u043E\u0433\u043B\u043E \u043D\u0435 \u0431\u044B\u0442\u044C \u0432\u043E\u0432\u0441\u0435, \u0430 \u043C\u043E\u0433\u043B\u043E \u0431\u044B\u0442\u044C \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0443\u0433\u043E\u0434\u043D\u043E.",
|
|
19600
|
+
"\u041F\u0443\u043D\u043A\u0442\u044B \u0432\u0438\u0434\u0430 \xAB\u0441\u0434\u0435\u043B\u0430\u043D\u043E \u043B\u0438 \u043A\u0430\u043A \u043D\u0430 \u043C\u0430\u043A\u0435\u0442\u0435\xBB \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u043C: \u0442\u0430\u043A \u0438 \u043D\u0430\u043F\u0438\u0448\u0438 \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0435, \u0430 \u043D\u0435 \u0441\u0443\u0434\u0438 \u043F\u043E \u043A\u043E\u0434\u0443."
|
|
19601
|
+
].join("\n");
|
|
19602
|
+
}
|
|
19603
|
+
return prepareTaskAttachments({
|
|
19604
|
+
worktree,
|
|
19605
|
+
refs: read.task.attachments,
|
|
19606
|
+
fetchFile: client.fetchFile.bind(client),
|
|
19607
|
+
// Выложенное и пропущенное — тоже в ленту проверяющего, как у исполнителя (respawnRun).
|
|
19608
|
+
...onEvent ? { onEvent } : {}
|
|
19609
|
+
});
|
|
19610
|
+
};
|
|
19611
|
+
}
|
|
19178
19612
|
async function spawnReviewer(deps, reviewRun, files) {
|
|
19179
19613
|
const repo = normalizeRepo(reviewRun.repo || deps.repoFallback);
|
|
19180
19614
|
const branch = await worktreeBranchFor(reviewRun, deps.prHeadBranch);
|
|
@@ -19197,11 +19631,16 @@ async function spawnReviewer(deps, reviewRun, files) {
|
|
|
19197
19631
|
// (своей у ревьюера нет, TASWM-204), и коммент с вердиктом должен уехать именно туда.
|
|
19198
19632
|
workspace_id: reviewRun.workspace_id
|
|
19199
19633
|
});
|
|
19200
|
-
const wt =
|
|
19634
|
+
const wt = join31(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-review-${reviewer.id}`);
|
|
19201
19635
|
try {
|
|
19202
19636
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
19203
19637
|
await deps.git.setWorktreeIdentity(wt, reviewer.name);
|
|
19204
|
-
const attachments = await deps.taskAttachments?.(
|
|
19638
|
+
const attachments = await deps.taskAttachments?.(
|
|
19639
|
+
reviewRun.task_slug,
|
|
19640
|
+
wt,
|
|
19641
|
+
reviewRun.workspace_id,
|
|
19642
|
+
(text) => deps.db.addEvent(reviewer.id, "step", text)
|
|
19643
|
+
);
|
|
19205
19644
|
const pendingRules = await deps.pendingRepoRules?.(repo, reviewRun.task_slug).catch(() => []) ?? [];
|
|
19206
19645
|
const prompt = buildReviewerPrompt(reviewRun, reviewer, checklist, branch, attachments, verifyMode, pendingRules);
|
|
19207
19646
|
const pid = deps.launch({ worktree: wt, run: reviewer, prompt });
|
|
@@ -19273,11 +19712,16 @@ async function spawnVerifier(deps, verifiedRun, files) {
|
|
|
19273
19712
|
// штамп цикла: фикс-итерацию проверяет свежий верификатор
|
|
19274
19713
|
workspace_id: verifiedRun.workspace_id
|
|
19275
19714
|
});
|
|
19276
|
-
const wt =
|
|
19715
|
+
const wt = join31(deps.worktreesDir, basename6(repo), `${verifiedRun.task_slug}-verify-${verifier.id}`);
|
|
19277
19716
|
try {
|
|
19278
19717
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
19279
19718
|
await deps.git.setWorktreeIdentity(wt, verifier.name);
|
|
19280
|
-
const attachments = await deps.taskAttachments?.(
|
|
19719
|
+
const attachments = await deps.taskAttachments?.(
|
|
19720
|
+
verifiedRun.task_slug,
|
|
19721
|
+
wt,
|
|
19722
|
+
verifiedRun.workspace_id,
|
|
19723
|
+
(text) => deps.db.addEvent(verifier.id, "step", text)
|
|
19724
|
+
);
|
|
19281
19725
|
const prompt = buildVerifierPrompt(verifiedRun, verifier, branch, files, attachments, verifyMode);
|
|
19282
19726
|
const pid = deps.launch({ worktree: wt, run: verifier, prompt });
|
|
19283
19727
|
deps.db.updateRun(verifier.id, { status: "running", worktree_path: wt, branch, pid, pr_url: verifiedRun.pr_url });
|
|
@@ -19314,7 +19758,7 @@ async function respawnRun(deps, run, mode, nudge) {
|
|
|
19314
19758
|
]);
|
|
19315
19759
|
const base3 = effectiveBaseBranch(run.base_branch, projectBase);
|
|
19316
19760
|
const runToSpawn = base3 && run.base_branch !== base3 ? deps.db.updateRun(run.id, { base_branch: base3 }) ?? run : run;
|
|
19317
|
-
if (!
|
|
19761
|
+
if (!existsSync18(wt)) {
|
|
19318
19762
|
const branch = runToSpawn.branch || runToSpawn.task_slug;
|
|
19319
19763
|
const source = await addWorktree(repo, wt, branch, {
|
|
19320
19764
|
...base3 ? { base: await remoteBaseRef(repo, base3) } : {},
|
|
@@ -19341,7 +19785,7 @@ async function respawnRun(deps, run, mode, nudge) {
|
|
|
19341
19785
|
deps.db.updateRun(run.id, { status: "running", pid });
|
|
19342
19786
|
}
|
|
19343
19787
|
async function syncCrashedOnStartup(deps) {
|
|
19344
|
-
const worktreeExists = deps.worktreeExists ??
|
|
19788
|
+
const worktreeExists = deps.worktreeExists ?? existsSync18;
|
|
19345
19789
|
let orphaned = 0;
|
|
19346
19790
|
let merged = 0;
|
|
19347
19791
|
let requeued = 0;
|
|
@@ -19404,7 +19848,7 @@ async function makeStore(repo, worktreesDir, repoFlag) {
|
|
|
19404
19848
|
console.warn(`[overmind] \u043D\u0435 \u0441\u043C\u043E\u0433 \u0437\u0430\u043F\u043E\u043C\u043D\u0438\u0442\u044C \u043F\u0443\u0442\u044C \u043A \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044E (${e instanceof Error ? e.message : e}) \u2014 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0437\u0430\u043F\u0443\u0441\u043A \u0441\u043D\u043E\u0432\u0430 \u043F\u043E\u043F\u0440\u043E\u0441\u0438\u0442 --repo`);
|
|
19405
19849
|
}
|
|
19406
19850
|
}
|
|
19407
|
-
const scanRootsList = resolveScanRoots(SWARM_SCAN_ROOTS_RAW,
|
|
19851
|
+
const scanRootsList = resolveScanRoots(SWARM_SCAN_ROOTS_RAW, homedir6(), repoPaths);
|
|
19408
19852
|
const repoMap = emptyRepoMapState(scanRootsList);
|
|
19409
19853
|
if (SWARM_BACKEND !== "client") {
|
|
19410
19854
|
const { SwarmDb: SwarmDb2 } = await loadSqlite();
|
|
@@ -19471,7 +19915,11 @@ async function makeStore(repo, worktreesDir, repoFlag) {
|
|
|
19471
19915
|
// TASWM-422: что машина обслуживает ПРЯМО СЕЙЧАС — читается из ЖИВОГО реестра (он
|
|
19472
19916
|
// пересобирается тиком выше), чтобы причина «клона нет на этой машине» называла
|
|
19473
19917
|
// актуальный список, а не снимок на старте демона.
|
|
19474
|
-
reposServed: () => repoRegistry.map((e) => e.repoFullName)
|
|
19918
|
+
reposServed: () => repoRegistry.map((e) => e.repoFullName),
|
|
19919
|
+
// TASWM-492: обратная сторона того же реестра — путь прогона в owner/repo. Читаем из
|
|
19920
|
+
// живого `repoRegistry` (не из снимка на старте): репозиторий могли клонировать уже
|
|
19921
|
+
// после запуска демона, и гейт мержа не должен считать его прогон «неопознанным».
|
|
19922
|
+
repoFullNameOf: (repoPath) => repoRegistry.find((e) => e.path === repoPath)?.repoFullName ?? null
|
|
19475
19923
|
};
|
|
19476
19924
|
}
|
|
19477
19925
|
var crashGuardsInstalled = false;
|
|
@@ -19491,7 +19939,7 @@ async function migrateWorktreesAtStartup() {
|
|
|
19491
19939
|
legacyDir: WORKTREES_LEGACY_DIR,
|
|
19492
19940
|
targetDir: WORKTREES_DIR,
|
|
19493
19941
|
envOverride: process.env.SWARM_WORKTREES || "",
|
|
19494
|
-
exists: (d) =>
|
|
19942
|
+
exists: (d) => existsSync18(d),
|
|
19495
19943
|
isNonEmptyDir: (d) => {
|
|
19496
19944
|
try {
|
|
19497
19945
|
return readdirSync13(d).length > 0;
|
|
@@ -19506,7 +19954,7 @@ async function migrateWorktreesAtStartup() {
|
|
|
19506
19954
|
});
|
|
19507
19955
|
} catch (e) {
|
|
19508
19956
|
console.error("[migrate] \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u044F worktrees \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C \u2014 \u0444\u043E\u043B\u0431\u044D\u043A:", e);
|
|
19509
|
-
return
|
|
19957
|
+
return existsSync18(WORKTREES_DIR) ? WORKTREES_DIR : WORKTREES_LEGACY_DIR;
|
|
19510
19958
|
}
|
|
19511
19959
|
}
|
|
19512
19960
|
async function reportWorkspaceAccess(tl, db) {
|
|
@@ -19547,7 +19995,8 @@ async function runDaemon(opts) {
|
|
|
19547
19995
|
repoMap: repoMapState,
|
|
19548
19996
|
refreshRepoMap: rebuildRepoMap,
|
|
19549
19997
|
prodMcp,
|
|
19550
|
-
reposServed
|
|
19998
|
+
reposServed,
|
|
19999
|
+
repoFullNameOf
|
|
19551
20000
|
} = await makeStore(opts.repo, worktreesDir, opts.repoFlag);
|
|
19552
20001
|
await preflightAuth();
|
|
19553
20002
|
const outdated = outdatedLine(PACKAGE_VERSION, await latestRelease);
|
|
@@ -19869,6 +20318,7 @@ async function runDaemon(opts) {
|
|
|
19869
20318
|
ttlMs: 6e4
|
|
19870
20319
|
});
|
|
19871
20320
|
}
|
|
20321
|
+
const checkerAttachments = tl?.fetchFile ? makeCheckerAttachmentsReader(tl) : void 0;
|
|
19872
20322
|
let spawnReviewerFn;
|
|
19873
20323
|
let verdictCommentFn;
|
|
19874
20324
|
let reviewCutoffAtValue;
|
|
@@ -19892,20 +20342,10 @@ async function runDaemon(opts) {
|
|
|
19892
20342
|
...pendingRepoRules ? { pendingRepoRules } : {},
|
|
19893
20343
|
// TASWM-283: те же вложения задачи, что у исполнителя — ревьюер без макета
|
|
19894
20344
|
// не может проверить «сделано ли как просили».
|
|
19895
|
-
|
|
19896
|
-
|
|
19897
|
-
|
|
19898
|
-
|
|
19899
|
-
taskAttachments: async (slug, worktree, workspaceId) => {
|
|
19900
|
-
const client = tl.forWorkspace(workspaceId);
|
|
19901
|
-
const task = await client.getTask(slug);
|
|
19902
|
-
return prepareTaskAttachments({
|
|
19903
|
-
worktree,
|
|
19904
|
-
refs: task?.attachments,
|
|
19905
|
-
fetchFile: client.fetchFile.bind(client)
|
|
19906
|
-
});
|
|
19907
|
-
}
|
|
19908
|
-
} : {}
|
|
20345
|
+
// TASWM-288: вложения читаем клиентом ВОРКСПЕЙСА проверяемого прогона — иначе
|
|
20346
|
+
// ревьюер задачи из чужого воркспейса остался бы без макетов («сделано ли как
|
|
20347
|
+
// просили» проверять нечем), как и исполнитель до фикса.
|
|
20348
|
+
...checkerAttachments ? { taskAttachments: checkerAttachments } : {}
|
|
19909
20349
|
},
|
|
19910
20350
|
reviewRun,
|
|
19911
20351
|
files
|
|
@@ -19927,17 +20367,7 @@ async function runDaemon(opts) {
|
|
|
19927
20367
|
git: { prepareDetachedWorktree, setWorktreeIdentity, removeWorktree },
|
|
19928
20368
|
prHeadBranch,
|
|
19929
20369
|
...verifyModeReader ? { verifyMode: verifyModeReader } : {},
|
|
19930
|
-
...
|
|
19931
|
-
taskAttachments: async (slug, worktree, workspaceId) => {
|
|
19932
|
-
const client = tl.forWorkspace(workspaceId);
|
|
19933
|
-
const task = await client.getTask(slug);
|
|
19934
|
-
return prepareTaskAttachments({
|
|
19935
|
-
worktree,
|
|
19936
|
-
refs: task?.attachments,
|
|
19937
|
-
fetchFile: client.fetchFile.bind(client)
|
|
19938
|
-
});
|
|
19939
|
-
}
|
|
19940
|
-
} : {}
|
|
20370
|
+
...checkerAttachments ? { taskAttachments: checkerAttachments } : {}
|
|
19941
20371
|
},
|
|
19942
20372
|
verifiedRun,
|
|
19943
20373
|
files
|
|
@@ -19976,6 +20406,13 @@ async function runDaemon(opts) {
|
|
|
19976
20406
|
const mergeParts = makeMergeGateParts({
|
|
19977
20407
|
listRuns: () => db.listRuns(),
|
|
19978
20408
|
...autoReviewProject ? { autoReview: autoReviewProject } : {},
|
|
20409
|
+
// TASWM-492: чей это PR. Номер PR у семи репозиториев роя независим, поэтому гейт
|
|
20410
|
+
// сверяет и репозиторий: owner/repo из pr_url, а короткий `#N` — по репозиторию
|
|
20411
|
+
// КОНТЕКСТА ВЫЗОВА (origin каталога, откуда спросили). repoFallback + репо-реестр
|
|
20412
|
+
// нужны для второй стороны сравнения: у прогона в базе лежит локальный путь, и пустой
|
|
20413
|
+
// он означает дефолт роя, а не «отдельный проект».
|
|
20414
|
+
repoFallback: opts.repo,
|
|
20415
|
+
...repoFullNameOf ? { repoFullNameOf } : {},
|
|
19979
20416
|
fallbackAutoReview: SWARM_AUTO_REVIEW,
|
|
19980
20417
|
autoVerify: SWARM_AUTO_VERIFY,
|
|
19981
20418
|
// TASWM-344: вторая ось приёмки — вердикт верификатора
|
|
@@ -20162,7 +20599,7 @@ async function runDaemon(opts) {
|
|
|
20162
20599
|
isDirty: (p) => worktreeDirty(p),
|
|
20163
20600
|
releaseWorktree: (repo, p) => releaseWorktree(repo || opts.repo, p),
|
|
20164
20601
|
pruneWorktrees: (repo) => pruneWorktrees(repo || opts.repo),
|
|
20165
|
-
worktreeExists:
|
|
20602
|
+
worktreeExists: existsSync18,
|
|
20166
20603
|
liveWorktrees: () => new Set(listDroneProcesses().map((p) => p.worktree)),
|
|
20167
20604
|
narrate: narrator.narrate
|
|
20168
20605
|
});
|
|
@@ -20285,7 +20722,7 @@ async function runDaemon(opts) {
|
|
|
20285
20722
|
mode,
|
|
20286
20723
|
nudge
|
|
20287
20724
|
),
|
|
20288
|
-
worktreeExists: (p) =>
|
|
20725
|
+
worktreeExists: (p) => existsSync18(p),
|
|
20289
20726
|
hasTranscript: executorHasSession,
|
|
20290
20727
|
// CI-осведомлённость review-PR (TASWM-102): реальный `gh pr checks`/`gh run view --log-failed`
|
|
20291
20728
|
// (gh.ts) — решение (будить ли дрона) и гейты живут в tickOnce/handleCiRed. failureSummary
|
|
@@ -20471,7 +20908,7 @@ async function runDaemon(opts) {
|
|
|
20471
20908
|
onStep
|
|
20472
20909
|
});
|
|
20473
20910
|
}
|
|
20474
|
-
const repo = await repoRoot(
|
|
20911
|
+
const repo = await repoRoot(dirname5(THIS_FILE)).catch(() => null);
|
|
20475
20912
|
if (!repo) {
|
|
20476
20913
|
return { ok: false, step: "fetch", message: "\u0431\u0435\u0433\u0443 \u0438\u0437 \u0438\u0441\u0445\u043E\u0434\u043D\u0438\u043A\u043E\u0432, \u043D\u043E git-\u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F \u043D\u0430\u0434 \u043D\u0438\u043C\u0438 \u043D\u0435\u0442 \u2014 \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E" };
|
|
20477
20914
|
}
|
|
@@ -20781,6 +21218,7 @@ export {
|
|
|
20781
21218
|
lastChangesSummary,
|
|
20782
21219
|
listDroneProcesses,
|
|
20783
21220
|
livePpids,
|
|
21221
|
+
makeCheckerAttachmentsReader,
|
|
20784
21222
|
replayDecision,
|
|
20785
21223
|
repoHasCiWorkflows,
|
|
20786
21224
|
resolveConflict,
|
|
@@ -20795,5 +21233,6 @@ export {
|
|
|
20795
21233
|
tickOnce,
|
|
20796
21234
|
verdictCommentMark,
|
|
20797
21235
|
wakeRun,
|
|
20798
|
-
worktreeFromCmd
|
|
21236
|
+
worktreeFromCmd,
|
|
21237
|
+
zoneConflictFor
|
|
20799
21238
|
};
|