@taskless-app/swarm 0.6.57 → 0.6.59
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/README.md +18 -0
- package/dist/cli.js +148 -48
- package/dist/overmind.js +355 -177
- package/package.json +2 -2
package/dist/overmind.js
CHANGED
|
@@ -665,6 +665,11 @@ function defaultModelFor(role, providerId = null) {
|
|
|
665
665
|
const provider = findProvider(providerId) ?? findProvider(DEFAULT_PROVIDER_ID);
|
|
666
666
|
return provider.roles[role];
|
|
667
667
|
}
|
|
668
|
+
function executorIdForModelRef(ref) {
|
|
669
|
+
const providerId = parseModelRef(ref)?.providerId ?? DEFAULT_PROVIDER_ID;
|
|
670
|
+
const provider = findProvider(providerId) ?? findProvider(DEFAULT_PROVIDER_ID);
|
|
671
|
+
return provider.executorId;
|
|
672
|
+
}
|
|
668
673
|
var HARNESS_DEFAULT_MODEL, MODEL_PROVIDERS, DEFAULT_PROVIDER_ID;
|
|
669
674
|
var init_modelPolicy = __esm({
|
|
670
675
|
"../swarm-core/src/modelPolicy.ts"() {
|
|
@@ -678,6 +683,21 @@ var init_modelPolicy = __esm({
|
|
|
678
683
|
selectable: true,
|
|
679
684
|
roles: { executor: "claude-opus-5-5", review: "claude-sonnet-5" }
|
|
680
685
|
},
|
|
686
|
+
// TASWM-593 · GLM (Z.ai), INTERIM: подписка GLM Coding Plan работает через официальный
|
|
687
|
+
// Anthropic-совместимый эндпоинт Z.ai (api.z.ai/api/anthropic), поэтому харнесс — тот же
|
|
688
|
+
// claude-code, а всё различие провайдера — env процесса дрона (BASE_URL + токен, см.
|
|
689
|
+
// providerEnv в адаптере; клаудовский аккаунт НЕ нужен — бинарь бьёт в Z.ai). Interim
|
|
690
|
+
// для роя владельца до появления `--model` у ZCode CLI: тогда провайдер переезжает на
|
|
691
|
+
// собственный zcode-адаптер (волт reference_zcode_cli_headless_research).
|
|
692
|
+
// Дефолты ролей: glm-5.3 — флагман; каталог подписки снаружи не виден, имя сверено
|
|
693
|
+
// живой пробой, выдуманных имён в реестре нет — настройкой можно поставить любую.
|
|
694
|
+
{
|
|
695
|
+
id: "zai",
|
|
696
|
+
label: "GLM (Z.ai)",
|
|
697
|
+
executorId: "claude-code",
|
|
698
|
+
selectable: true,
|
|
699
|
+
roles: { executor: "glm-5.3", review: "glm-5.3" }
|
|
700
|
+
},
|
|
681
701
|
// TASWM-565 · шаг 1 из четырёх: провайдер в реестре ЕСТЬ, выбрать его ещё нельзя.
|
|
682
702
|
// Имён моделей здесь нет СОЗНАТЕЛЬНО: у владельца подписка, состав моделей в ней нам
|
|
683
703
|
// неизвестен, а выдуманное имя хуже маркера «решает Codex».
|
|
@@ -2515,10 +2535,25 @@ function writeMcpConfig(spec) {
|
|
|
2515
2535
|
writeFileSync3(path, JSON.stringify(cfg, null, 2));
|
|
2516
2536
|
return path;
|
|
2517
2537
|
}
|
|
2538
|
+
function providerEnv(providerId, model, env = process.env) {
|
|
2539
|
+
const compat = ANTHROPIC_COMPAT_PROVIDERS[providerId];
|
|
2540
|
+
if (!compat) return {};
|
|
2541
|
+
const token = (env[compat.tokenEnv] ?? "").trim();
|
|
2542
|
+
if (!token) {
|
|
2543
|
+
throw new Error(
|
|
2544
|
+
`\u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 "${providerId}": \u043D\u0435 \u0437\u0430\u0434\u0430\u043D ${compat.tokenEnv} \u2014 \u043A\u043B\u044E\u0447 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438 \u043A\u043B\u0430\u0434\u0451\u0442\u0441\u044F \u0432 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u0414\u0415\u041C\u041E\u041D\u0410 (env \u0437\u0430\u043F\u0443\u0441\u043A\u0430 \u0438\u043B\u0438 .env \u043C\u0430\u0448\u0438\u043D\u044B), \u0432 \u0442\u0440\u0435\u043A\u0435\u0440\u0435 \u043E\u043D \u043D\u0435 \u0445\u0440\u0430\u043D\u0438\u0442\u0441\u044F. \u0411\u0435\u0437 \u043A\u043B\u044E\u0447\u0430 \u0434\u0440\u043E\u043D \u0443\u043C\u0440\u0451\u0442 \u043D\u0430 \u043F\u0435\u0440\u0432\u043E\u043C \u0436\u0435 \u0437\u0430\u043F\u0440\u043E\u0441\u0435 \u0438 \u0441\u043E\u0439\u0434\u0451\u0442 \u0437\u0430 \u0441\u0431\u043E\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u2014 \u043D\u0435 \u0441\u043F\u0430\u0432\u043D\u0438\u043C \u0432\u043E\u0432\u0441\u0435.`
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2547
|
+
return {
|
|
2548
|
+
ANTHROPIC_BASE_URL: (env[compat.baseUrlEnv] ?? "").trim() || compat.baseUrlDefault,
|
|
2549
|
+
ANTHROPIC_AUTH_TOKEN: token,
|
|
2550
|
+
...model ? { ANTHROPIC_MODEL: model, ANTHROPIC_SMALL_FAST_MODEL: model } : {}
|
|
2551
|
+
};
|
|
2552
|
+
}
|
|
2518
2553
|
function abortField(abort) {
|
|
2519
2554
|
return abort ? { aborted: abort } : {};
|
|
2520
2555
|
}
|
|
2521
|
-
var ISOLATION_FLAGS, MCP_CONFIG_FILE, claudeCodeExecutor;
|
|
2556
|
+
var ISOLATION_FLAGS, MCP_CONFIG_FILE, ANTHROPIC_COMPAT_PROVIDERS, PROVIDER_TOKEN_ENV, claudeCodeExecutor;
|
|
2522
2557
|
var init_claudeCode = __esm({
|
|
2523
2558
|
"src/executors/claudeCode.ts"() {
|
|
2524
2559
|
"use strict";
|
|
@@ -2529,21 +2564,31 @@ var init_claudeCode = __esm({
|
|
|
2529
2564
|
init_transcriptSpend();
|
|
2530
2565
|
ISOLATION_FLAGS = ["--setting-sources", "project", "--strict-mcp-config"];
|
|
2531
2566
|
MCP_CONFIG_FILE = SWARM_MCP_CONFIG_FILE;
|
|
2567
|
+
ANTHROPIC_COMPAT_PROVIDERS = {
|
|
2568
|
+
zai: {
|
|
2569
|
+
baseUrlDefault: "https://api.z.ai/api/anthropic",
|
|
2570
|
+
baseUrlEnv: "SWARM_ZAI_BASE_URL",
|
|
2571
|
+
tokenEnv: "SWARM_ZAI_TOKEN"
|
|
2572
|
+
}
|
|
2573
|
+
};
|
|
2574
|
+
PROVIDER_TOKEN_ENV = Object.values(ANTHROPIC_COMPAT_PROVIDERS).map((p) => p.tokenEnv);
|
|
2532
2575
|
claudeCodeExecutor = {
|
|
2533
2576
|
id: "claude-code",
|
|
2534
2577
|
title: "Claude Code (claude)",
|
|
2535
2578
|
binEnv: "SWARM_CLAUDE_BIN",
|
|
2536
2579
|
install: { what: "Claude Code CLI \u2014 \u0438\u043C \u0434\u0440\u043E\u043D \u043F\u0438\u0448\u0435\u0442 \u043A\u043E\u0434", pkg: "@anthropic-ai/claude-code" },
|
|
2537
|
-
resolveBin: () => resolveClaudeBin(),
|
|
2580
|
+
resolveBin: (env) => resolveClaudeBin(env ? { env } : {}),
|
|
2538
2581
|
// Стрелка, а не метод: `plan` могут вызвать в отрыве от объекта (деструктуризация в тестах,
|
|
2539
2582
|
// проброс колбэком), и `this` тогда уедет в undefined.
|
|
2540
2583
|
plan: (spec) => {
|
|
2541
2584
|
const cfgPath = writeMcpConfig(spec);
|
|
2542
2585
|
const hook = droneMergeGateHookPath(spec.worktree);
|
|
2543
2586
|
const settings = spec.denyPaths.length > 0 || hook ? droneSettingsJson(spec.denyPaths, hook) : void 0;
|
|
2587
|
+
const providerEnvVars = providerEnv(spec.providerId, spec.model);
|
|
2544
2588
|
return {
|
|
2545
2589
|
bin: resolveClaudeBin(),
|
|
2546
|
-
args: claudeArgs(cfgPath, spec.prompt, spec.continueSession, spec.model, spec.skillDirs, settings, spec.maxBudgetUsd, spec.effort)
|
|
2590
|
+
args: claudeArgs(cfgPath, spec.prompt, spec.continueSession, spec.model, spec.skillDirs, settings, spec.maxBudgetUsd, spec.effort),
|
|
2591
|
+
...Object.keys(providerEnvVars).length > 0 ? { env: providerEnvVars } : {}
|
|
2547
2592
|
};
|
|
2548
2593
|
},
|
|
2549
2594
|
parseResult(raw, model) {
|
|
@@ -2595,7 +2640,12 @@ var init_claudeCode = __esm({
|
|
|
2595
2640
|
probe: {
|
|
2596
2641
|
args: [...ISOLATION_FLAGS, "-p", "ok", "--max-turns", "1"],
|
|
2597
2642
|
// TASWM-214: исчерпанный лимит подписки — НЕ шторм провайдера, и путать их нельзя.
|
|
2598
|
-
limitPattern: /(weekly limit|usage limit|hit your .*limit)/i
|
|
2643
|
+
limitPattern: /(weekly limit|usage limit|hit your .*limit)/i,
|
|
2644
|
+
// TASWM-592: отвергнутый доступ — не «шторм»: ключ протух, аккаунт забанен/отключён,
|
|
2645
|
+
// регион закрыт (403 без VPN — живой кейс этого роя). Коды — только в контексте
|
|
2646
|
+
// ошибки (ревью PR #1482): голое «403» в arbitrary-выводе и POSIX permission denied
|
|
2647
|
+
// от локальной ФС советовали бы «чинить вход» не про ту болезнь.
|
|
2648
|
+
authPattern: /(?:error|api|http|status|unauthorized|forbidden)[^\n]{0,60}\b40[13]\b|\b40[13]\b[^\n]{0,60}(?:error|unauthorized|forbidden)|authentication_?error|unauthorized|invalid api key|account[^.\n]{0,40}?(?:suspended|banned|disabled)/i
|
|
2599
2649
|
}
|
|
2600
2650
|
};
|
|
2601
2651
|
}
|
|
@@ -2726,7 +2776,7 @@ var init_codex = __esm({
|
|
|
2726
2776
|
title: "Codex CLI (codex)",
|
|
2727
2777
|
binEnv: "SWARM_CODEX_BIN",
|
|
2728
2778
|
install: { what: "Codex CLI \u2014 \u0430\u043B\u044C\u0442\u0435\u0440\u043D\u0430\u0442\u0438\u0432\u043D\u044B\u0439 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C \u0434\u0440\u043E\u043D\u0430", pkg: "@openai/codex" },
|
|
2729
|
-
resolveBin: () => resolveCodexBin(),
|
|
2779
|
+
resolveBin: (env) => resolveCodexBin(env ?? process.env),
|
|
2730
2780
|
// Стрелка, а не метод — см. тот же комментарий в claudeCode.ts.
|
|
2731
2781
|
plan: (spec) => {
|
|
2732
2782
|
const notice = skillsNotice(spec.skillDirs);
|
|
@@ -2775,12 +2825,48 @@ ${notice}` : spec.prompt;
|
|
|
2775
2825
|
// Тот же принцип, что у claude: дешёвый реальный прогон — он проверяет и auth, и живость
|
|
2776
2826
|
// провайдера разом. Один короткий ответ, без тулов и без сети сверх модели.
|
|
2777
2827
|
args: ["exec", "--json", SKIP_GIT_CHECK_FLAG, BYPASS_FLAG, "ok"],
|
|
2828
|
+
// TASWM-592: не вошёл/ключ отвергнут — не шторм; точные формулировки codex уточним
|
|
2829
|
+
// на живом исполнителе, поэтому паттерн широкий.
|
|
2830
|
+
authPattern: /\b(401|403)\b|not logged in|unauthorized|authentication|invalid api key/i,
|
|
2778
2831
|
limitPattern: /(usage limit|rate limit|quota exceeded|insufficient_quota)/i
|
|
2779
2832
|
}
|
|
2780
2833
|
};
|
|
2781
2834
|
}
|
|
2782
2835
|
});
|
|
2783
2836
|
|
|
2837
|
+
// src/swarmModels.ts
|
|
2838
|
+
function pick2(v) {
|
|
2839
|
+
const s = typeof v === "string" ? v.trim() : "";
|
|
2840
|
+
return s.length > 0 ? s : null;
|
|
2841
|
+
}
|
|
2842
|
+
function isReviewKind(kind) {
|
|
2843
|
+
return kind === "reviewer" || kind === "verifier";
|
|
2844
|
+
}
|
|
2845
|
+
function modelRoleOf(kind) {
|
|
2846
|
+
return isReviewKind(kind) ? "review" : "executor";
|
|
2847
|
+
}
|
|
2848
|
+
function forKind(kind, swarm, norm) {
|
|
2849
|
+
if (!swarm) return null;
|
|
2850
|
+
return isReviewKind(kind) ? norm(swarm.reviewer) ?? norm(swarm.drone) : norm(swarm.drone);
|
|
2851
|
+
}
|
|
2852
|
+
function resolveRunModelRef(run, swarm, env = process.env) {
|
|
2853
|
+
const configured = parseModelRef(
|
|
2854
|
+
pick2(run.model) ?? forKind(run.kind, swarm, pick2) ?? pick2(env.SWARM_DRONE_MODEL)
|
|
2855
|
+
);
|
|
2856
|
+
if (configured) return configured;
|
|
2857
|
+
const providerId = pick2(env.SWARM_MODEL_PROVIDER) ?? DEFAULT_PROVIDER_ID;
|
|
2858
|
+
return { providerId, model: defaultModelFor(modelRoleOf(run.kind), providerId) };
|
|
2859
|
+
}
|
|
2860
|
+
function resolveRunEffort(run, swarm, env = process.env) {
|
|
2861
|
+
return toEffortLevel(run.effort) ?? forKind(run.kind, swarm, toEffortLevel) ?? toEffortLevel(env.SWARM_DRONE_EFFORT) ?? null;
|
|
2862
|
+
}
|
|
2863
|
+
var init_swarmModels = __esm({
|
|
2864
|
+
"src/swarmModels.ts"() {
|
|
2865
|
+
"use strict";
|
|
2866
|
+
init_src();
|
|
2867
|
+
}
|
|
2868
|
+
});
|
|
2869
|
+
|
|
2784
2870
|
// src/executors/types.ts
|
|
2785
2871
|
var init_types2 = __esm({
|
|
2786
2872
|
"src/executors/types.ts"() {
|
|
@@ -2798,12 +2884,22 @@ function resolveExecutor(id) {
|
|
|
2798
2884
|
}
|
|
2799
2885
|
return found;
|
|
2800
2886
|
}
|
|
2887
|
+
function executorForRun(run, swarm, env = process.env, pinned = null) {
|
|
2888
|
+
const ref = pinned ?? resolveRunModelRef(run, swarm, env);
|
|
2889
|
+
if (ref.providerId === DEFAULT_PROVIDER_ID) {
|
|
2890
|
+
const envExecutor = (env.SWARM_EXECUTOR ?? "").trim();
|
|
2891
|
+
if (envExecutor) return resolveExecutor(envExecutor);
|
|
2892
|
+
}
|
|
2893
|
+
return resolveExecutor(executorIdForModelRef(`${ref.providerId}/${ref.model}`));
|
|
2894
|
+
}
|
|
2801
2895
|
var DEFAULT_EXECUTOR_ID, EXECUTORS;
|
|
2802
2896
|
var init_executors = __esm({
|
|
2803
2897
|
"src/executors/index.ts"() {
|
|
2804
2898
|
"use strict";
|
|
2899
|
+
init_src();
|
|
2805
2900
|
init_claudeCode();
|
|
2806
2901
|
init_codex();
|
|
2902
|
+
init_swarmModels();
|
|
2807
2903
|
init_types2();
|
|
2808
2904
|
init_claudeCode();
|
|
2809
2905
|
init_codex();
|
|
@@ -4273,7 +4369,7 @@ var init_procCleanup = __esm({
|
|
|
4273
4369
|
// src/git.ts
|
|
4274
4370
|
import { existsSync as existsSync12, readdirSync as readdirSync12, realpathSync as realpathSync2, statSync as statSync10 } from "fs";
|
|
4275
4371
|
import { appendFile, chmod, mkdir, readFile, rename, rm, writeFile } from "fs/promises";
|
|
4276
|
-
import { isAbsolute as
|
|
4372
|
+
import { isAbsolute as isAbsolute3, join as join21, resolve as resolve3, sep as sep2 } from "path";
|
|
4277
4373
|
import { $ as $2 } from "execa";
|
|
4278
4374
|
function gitNet(repo, args, timeoutMs) {
|
|
4279
4375
|
return withHardTimeout(
|
|
@@ -4302,7 +4398,7 @@ async function inspectWorktreePath(path) {
|
|
|
4302
4398
|
return "orphan";
|
|
4303
4399
|
}
|
|
4304
4400
|
try {
|
|
4305
|
-
if (statSync10(
|
|
4401
|
+
if (statSync10(join21(path, ".git")).isDirectory()) return "main-checkout";
|
|
4306
4402
|
} catch {
|
|
4307
4403
|
}
|
|
4308
4404
|
const top = await $2({ reject: false })`git -C ${path} rev-parse --show-toplevel`;
|
|
@@ -4362,7 +4458,7 @@ async function parkOrphanDir(repo, path, branch) {
|
|
|
4362
4458
|
}
|
|
4363
4459
|
function isLinkedWorktree(dir) {
|
|
4364
4460
|
try {
|
|
4365
|
-
return statSync10(
|
|
4461
|
+
return statSync10(join21(dir, ".git")).isFile();
|
|
4366
4462
|
} catch {
|
|
4367
4463
|
return false;
|
|
4368
4464
|
}
|
|
@@ -4474,8 +4570,8 @@ async function ensureGitExcluded(worktree, pattern) {
|
|
|
4474
4570
|
try {
|
|
4475
4571
|
const commonDir = await gitPath(worktree, "--git-common-dir");
|
|
4476
4572
|
if (!commonDir) return false;
|
|
4477
|
-
const infoDir =
|
|
4478
|
-
const excludeFile =
|
|
4573
|
+
const infoDir = join21(commonDir, "info");
|
|
4574
|
+
const excludeFile = join21(infoDir, "exclude");
|
|
4479
4575
|
let current = "";
|
|
4480
4576
|
try {
|
|
4481
4577
|
current = await readFile(excludeFile, "utf8");
|
|
@@ -4495,7 +4591,7 @@ async function gitPath(worktree, flag) {
|
|
|
4495
4591
|
const out = await $2({ reject: false })`git -C ${worktree} rev-parse ${flag}`;
|
|
4496
4592
|
const raw = out.stdout.trim();
|
|
4497
4593
|
if (out.exitCode !== 0 || !raw) return null;
|
|
4498
|
-
return
|
|
4594
|
+
return isAbsolute3(raw) ? raw : join21(worktree, raw);
|
|
4499
4595
|
}
|
|
4500
4596
|
async function ensureWorktreeHygiene(worktree) {
|
|
4501
4597
|
for (const pattern of SWARM_EXCLUDE_PATTERNS) await ensureGitExcluded(worktree, pattern);
|
|
@@ -4505,12 +4601,12 @@ async function installGuardHooks(worktree) {
|
|
|
4505
4601
|
try {
|
|
4506
4602
|
const gitDir = await gitPath(worktree, "--git-dir");
|
|
4507
4603
|
if (!gitDir) return false;
|
|
4508
|
-
const hooksDir =
|
|
4604
|
+
const hooksDir = join21(gitDir, "swarm-hooks");
|
|
4509
4605
|
const prev = await previousHooksPath(worktree);
|
|
4510
4606
|
await mkdir(hooksDir, { recursive: true });
|
|
4511
4607
|
for (const hook of GUARD_HOOKS) {
|
|
4512
|
-
await writeFile(
|
|
4513
|
-
await chmod(
|
|
4608
|
+
await writeFile(join21(hooksDir, hook), guardHookScript(hook, prev ? `${posix(prev)}/${hook}` : null));
|
|
4609
|
+
await chmod(join21(hooksDir, hook), 493);
|
|
4514
4610
|
}
|
|
4515
4611
|
await $2`git -C ${worktree} config extensions.worktreeConfig true`;
|
|
4516
4612
|
await $2`git -C ${worktree} config --worktree core.hooksPath ${posix(hooksDir)}`;
|
|
@@ -4529,7 +4625,7 @@ async function previousHooksPath(worktree) {
|
|
|
4529
4625
|
if (out.exitCode === 0 && value) return value;
|
|
4530
4626
|
}
|
|
4531
4627
|
const commonDir = await gitPath(worktree, "--git-common-dir");
|
|
4532
|
-
return commonDir ?
|
|
4628
|
+
return commonDir ? join21(commonDir, "hooks") : null;
|
|
4533
4629
|
}
|
|
4534
4630
|
async function removeWorktree(repo, path) {
|
|
4535
4631
|
const branch = (await $2({ reject: false })`git -C ${path} symbolic-ref --quiet --short HEAD`).stdout.trim();
|
|
@@ -4554,7 +4650,7 @@ async function worktreeDirty(path) {
|
|
|
4554
4650
|
async function repairMovedWorktrees(targetDir) {
|
|
4555
4651
|
const subdirs = (dir) => {
|
|
4556
4652
|
try {
|
|
4557
|
-
return readdirSync12(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) =>
|
|
4653
|
+
return readdirSync12(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join21(dir, d.name));
|
|
4558
4654
|
} catch {
|
|
4559
4655
|
return [];
|
|
4560
4656
|
}
|
|
@@ -5092,7 +5188,7 @@ var init_droneChannel = __esm({
|
|
|
5092
5188
|
|
|
5093
5189
|
// src/attachments.ts
|
|
5094
5190
|
import { mkdir as mkdir3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
5095
|
-
import { join as
|
|
5191
|
+
import { join as join25 } from "path";
|
|
5096
5192
|
function attachmentsFromDoc(doc, origin) {
|
|
5097
5193
|
const out = [];
|
|
5098
5194
|
const walk2 = (node) => {
|
|
@@ -5183,7 +5279,7 @@ async function materializeAttachments(opts) {
|
|
|
5183
5279
|
};
|
|
5184
5280
|
const result = { files: [], skipped: [] };
|
|
5185
5281
|
if (opts.refs.length === 0) return result;
|
|
5186
|
-
const dir =
|
|
5282
|
+
const dir = join25(opts.worktree, ATTACHMENTS_DIR);
|
|
5187
5283
|
await io.removeDir(dir);
|
|
5188
5284
|
let dirReady = false;
|
|
5189
5285
|
let total = 0;
|
|
@@ -5229,7 +5325,7 @@ async function materializeAttachments(opts) {
|
|
|
5229
5325
|
await io.mkdir(dir);
|
|
5230
5326
|
dirReady = true;
|
|
5231
5327
|
}
|
|
5232
|
-
await io.writeFile(
|
|
5328
|
+
await io.writeFile(join25(dir, name), file.bytes);
|
|
5233
5329
|
} catch (e) {
|
|
5234
5330
|
result.skipped.push(`${label2} \u2014 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B\u043E\u0441\u044C \u043D\u0430 \u0434\u0438\u0441\u043A (${errText2(e)})`);
|
|
5235
5331
|
continue;
|
|
@@ -5382,7 +5478,7 @@ var init_attachments = __esm({
|
|
|
5382
5478
|
|
|
5383
5479
|
// src/shotUpload.ts
|
|
5384
5480
|
import { readFile as fsReadFile } from "fs/promises";
|
|
5385
|
-
import { basename as basename4, isAbsolute as
|
|
5481
|
+
import { basename as basename4, isAbsolute as isAbsolute4, join as join26 } from "path";
|
|
5386
5482
|
function shotMimetype(path) {
|
|
5387
5483
|
const dot = path.lastIndexOf(".");
|
|
5388
5484
|
if (dot < 0) return null;
|
|
@@ -5408,7 +5504,7 @@ async function readShots(paths, opts) {
|
|
|
5408
5504
|
}
|
|
5409
5505
|
let bytes;
|
|
5410
5506
|
try {
|
|
5411
|
-
bytes = await read(
|
|
5507
|
+
bytes = await read(isAbsolute4(path) ? path : join26(opts.worktree, path));
|
|
5412
5508
|
} catch (e) {
|
|
5413
5509
|
skipped.push({ path, reason: `\u043D\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D: ${e instanceof Error ? e.message : String(e)}` });
|
|
5414
5510
|
continue;
|
|
@@ -5490,7 +5586,7 @@ var init_shotUpload = __esm({
|
|
|
5490
5586
|
// src/oauth.ts
|
|
5491
5587
|
import { createHash as createHash3, randomBytes as randomBytes2 } from "crypto";
|
|
5492
5588
|
import { readFileSync as readFileSync20, writeFileSync as writeFileSync13, unlinkSync as unlinkSync4 } from "fs";
|
|
5493
|
-
import { join as
|
|
5589
|
+
import { join as join27 } from "path";
|
|
5494
5590
|
function makePkce() {
|
|
5495
5591
|
const verifier = randomBytes2(48).toString("base64url");
|
|
5496
5592
|
const challenge = createHash3("sha256").update(verifier).digest("base64url");
|
|
@@ -5625,8 +5721,8 @@ var init_oauth = __esm({
|
|
|
5625
5721
|
AUTHORIZE = "/api/oauth/authorize";
|
|
5626
5722
|
TOKEN = "/api/oauth/token";
|
|
5627
5723
|
SCOPE = "openid profile email taskless:full";
|
|
5628
|
-
STORE_PATH2 =
|
|
5629
|
-
CLIENT_PATH =
|
|
5724
|
+
STORE_PATH2 = join27(SWARM_HOME, "oauth.json");
|
|
5725
|
+
CLIENT_PATH = join27(SWARM_HOME, "oauth-client.json");
|
|
5630
5726
|
base2 = (api) => api.replace(/\/$/, "");
|
|
5631
5727
|
}
|
|
5632
5728
|
});
|
|
@@ -5673,7 +5769,7 @@ var init_authState = __esm({
|
|
|
5673
5769
|
|
|
5674
5770
|
// src/workspace.ts
|
|
5675
5771
|
import { readFileSync as readFileSync21, writeFileSync as writeFileSync14, unlinkSync as unlinkSync5 } from "fs";
|
|
5676
|
-
import { join as
|
|
5772
|
+
import { join as join28 } from "path";
|
|
5677
5773
|
function loadWorkspace() {
|
|
5678
5774
|
try {
|
|
5679
5775
|
const w = JSON.parse(readFileSync21(WORKSPACE_PATH, "utf8"));
|
|
@@ -5690,7 +5786,7 @@ var init_workspace = __esm({
|
|
|
5690
5786
|
"src/workspace.ts"() {
|
|
5691
5787
|
"use strict";
|
|
5692
5788
|
init_config();
|
|
5693
|
-
WORKSPACE_PATH =
|
|
5789
|
+
WORKSPACE_PATH = join28(SWARM_HOME, "workspace.json");
|
|
5694
5790
|
}
|
|
5695
5791
|
});
|
|
5696
5792
|
|
|
@@ -7322,7 +7418,7 @@ var init_droneStore = __esm({
|
|
|
7322
7418
|
// src/overmind.ts
|
|
7323
7419
|
import { homedir as homedir7 } from "os";
|
|
7324
7420
|
import { existsSync as existsSync20, readFileSync as readFileSync25, readdirSync as readdirSync14, renameSync as renameSync12, statSync as statSync13 } from "fs";
|
|
7325
|
-
import { basename as basename6, dirname as dirname5, join as
|
|
7421
|
+
import { basename as basename6, dirname as dirname5, join as join35 } from "path";
|
|
7326
7422
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
|
|
7327
7423
|
import { spawn as spawn4 } from "child_process";
|
|
7328
7424
|
|
|
@@ -9500,7 +9596,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
9500
9596
|
*
|
|
9501
9597
|
* Прод недоступен / ответ без поручений → пустые списки (демон работает дальше).
|
|
9502
9598
|
*/
|
|
9503
|
-
async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick) {
|
|
9599
|
+
async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick, executors) {
|
|
9504
9600
|
try {
|
|
9505
9601
|
const args = this.repoFullNames.length ? { repoFullNames: this.repoFullNames } : {};
|
|
9506
9602
|
if (daemonVersion) args.daemonVersion = daemonVersion;
|
|
@@ -9530,6 +9626,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
9530
9626
|
if (tick && Number.isFinite(tick.seq) && Number.isFinite(tick.ageMs) && tick.intervalMs > 0) {
|
|
9531
9627
|
args.tick = { seq: Math.max(0, Math.trunc(tick.seq)), ageMs: Math.max(0, Math.trunc(tick.ageMs)), intervalMs: Math.trunc(tick.intervalMs) };
|
|
9532
9628
|
}
|
|
9629
|
+
if (executors) args.executorsAvailable = executors;
|
|
9533
9630
|
const res = await this.call("swarm_heartbeat", args);
|
|
9534
9631
|
if (res?.isError) return EMPTY_HEARTBEAT_REPLY;
|
|
9535
9632
|
this.heartbeatOkAt = this.now();
|
|
@@ -11179,39 +11276,10 @@ function droneTestEnv(droneCap, base3 = process.env, host = hostForkSpec()) {
|
|
|
11179
11276
|
};
|
|
11180
11277
|
}
|
|
11181
11278
|
|
|
11182
|
-
// src/swarmModels.ts
|
|
11183
|
-
init_src();
|
|
11184
|
-
function pick2(v) {
|
|
11185
|
-
const s = typeof v === "string" ? v.trim() : "";
|
|
11186
|
-
return s.length > 0 ? s : null;
|
|
11187
|
-
}
|
|
11188
|
-
function isReviewKind(kind) {
|
|
11189
|
-
return kind === "reviewer" || kind === "verifier";
|
|
11190
|
-
}
|
|
11191
|
-
function modelRoleOf(kind) {
|
|
11192
|
-
return isReviewKind(kind) ? "review" : "executor";
|
|
11193
|
-
}
|
|
11194
|
-
function forKind(kind, swarm, norm) {
|
|
11195
|
-
if (!swarm) return null;
|
|
11196
|
-
return isReviewKind(kind) ? norm(swarm.reviewer) ?? norm(swarm.drone) : norm(swarm.drone);
|
|
11197
|
-
}
|
|
11198
|
-
function resolveRunModelRef(run, swarm, env = process.env) {
|
|
11199
|
-
const configured = parseModelRef(
|
|
11200
|
-
pick2(run.model) ?? forKind(run.kind, swarm, pick2) ?? pick2(env.SWARM_DRONE_MODEL)
|
|
11201
|
-
);
|
|
11202
|
-
if (configured) return configured;
|
|
11203
|
-
const providerId = pick2(env.SWARM_MODEL_PROVIDER) ?? DEFAULT_PROVIDER_ID;
|
|
11204
|
-
return { providerId, model: defaultModelFor(modelRoleOf(run.kind), providerId) };
|
|
11205
|
-
}
|
|
11206
|
-
function resolveRunModel(run, swarm, env = process.env) {
|
|
11207
|
-
return resolveRunModelRef(run, swarm, env).model;
|
|
11208
|
-
}
|
|
11209
|
-
function resolveRunEffort(run, swarm, env = process.env) {
|
|
11210
|
-
return toEffortLevel(run.effort) ?? forKind(run.kind, swarm, toEffortLevel) ?? toEffortLevel(env.SWARM_DRONE_EFFORT) ?? null;
|
|
11211
|
-
}
|
|
11212
|
-
|
|
11213
11279
|
// src/launcher.ts
|
|
11280
|
+
init_swarmModels();
|
|
11214
11281
|
init_executors();
|
|
11282
|
+
init_claudeCode();
|
|
11215
11283
|
init_src();
|
|
11216
11284
|
var requireFromHere = createRequire(import.meta.url);
|
|
11217
11285
|
function resolveTsxCli() {
|
|
@@ -11262,7 +11330,10 @@ var DAEMON_STATE_ENV = [
|
|
|
11262
11330
|
"SWARM_OUTBOX",
|
|
11263
11331
|
"SWARM_HEARTBEAT",
|
|
11264
11332
|
"SWARM_REVIEW_CUTOFF",
|
|
11265
|
-
"TASKLESS_TOKEN"
|
|
11333
|
+
"TASKLESS_TOKEN",
|
|
11334
|
+
// TASWM-593 (ревью PR #1483): машинные ключи совместимых провайдеров — дрону не наследуются;
|
|
11335
|
+
// доходит только переведённый ANTHROPIC_AUTH_TOKEN из plan.env, который мешается ПОСЛЕ чистки.
|
|
11336
|
+
...PROVIDER_TOKEN_ENV
|
|
11266
11337
|
];
|
|
11267
11338
|
function bundledRoleDir(kind) {
|
|
11268
11339
|
return join17(dirname3(fileURLToPath2(import.meta.url)), "..", "roles", kind);
|
|
@@ -11361,13 +11432,18 @@ function makeLauncher(cfg) {
|
|
|
11361
11432
|
...run.hero_run_id != null ? { SWARM_HERO_RUN_ID: String(run.hero_run_id) } : {}
|
|
11362
11433
|
}
|
|
11363
11434
|
};
|
|
11364
|
-
const
|
|
11435
|
+
const modelRef = resolveRunModelRef(run, cfg.swarmModels?.() ?? null);
|
|
11365
11436
|
const effort = resolveRunEffort(run, cfg.swarmEfforts?.() ?? null);
|
|
11366
11437
|
try {
|
|
11367
11438
|
cfg.recordEffort?.(run.id, effort);
|
|
11368
11439
|
} catch (e) {
|
|
11369
11440
|
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)}`);
|
|
11370
11441
|
}
|
|
11442
|
+
try {
|
|
11443
|
+
cfg.recordModelRef?.(run.id, modelRef);
|
|
11444
|
+
} catch (e) {
|
|
11445
|
+
console.error(`[launcher] \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u043C\u043E\u0434\u0435\u043B\u044C \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${run.id}: ${String(e)}`);
|
|
11446
|
+
}
|
|
11371
11447
|
const realHome = cfg.droneIsolation?.realHome ?? homedir6();
|
|
11372
11448
|
const home = cfg.droneIsolation ? buildDroneHome({ realHome, droneHome: cfg.droneIsolation.home }) : void 0;
|
|
11373
11449
|
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync11(join17(cfg.droneIsolation.swarmHome, "token"))) {
|
|
@@ -11375,12 +11451,13 @@ function makeLauncher(cfg) {
|
|
|
11375
11451
|
`[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 ${join17(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.`
|
|
11376
11452
|
);
|
|
11377
11453
|
}
|
|
11378
|
-
const executor = cfg.executor ??
|
|
11454
|
+
const executor = cfg.executor ?? executorForRun(run, cfg.swarmModels?.() ?? null);
|
|
11379
11455
|
const plan = executor.plan({
|
|
11380
11456
|
worktree,
|
|
11381
11457
|
prompt,
|
|
11382
11458
|
continueSession: !!continueSession,
|
|
11383
|
-
model,
|
|
11459
|
+
model: modelRef.model,
|
|
11460
|
+
providerId: modelRef.providerId,
|
|
11384
11461
|
mcp,
|
|
11385
11462
|
skillDirs: roleDirs(worktree, run.kind),
|
|
11386
11463
|
// Закрытые дрону пути имеют смысл только при теневом HOME: без него дрон и так
|
|
@@ -11436,6 +11513,7 @@ init_procCleanup();
|
|
|
11436
11513
|
import { readFileSync as readFileSync15, readdirSync as readdirSync10, renameSync as renameSync9, statSync as statSync9 } from "fs";
|
|
11437
11514
|
import { join as join18 } from "path";
|
|
11438
11515
|
init_executors();
|
|
11516
|
+
init_swarmModels();
|
|
11439
11517
|
var UNPROCESSED_LOG_RE = /^\d+\.json$/;
|
|
11440
11518
|
var SEGMENT_LOG_RE = /^(\d+)\.json(\.done)?$/;
|
|
11441
11519
|
var RESCUE_OPEN_WINDOW_MAX_MS = 12 * 60 * 60 * 1e3;
|
|
@@ -11499,8 +11577,9 @@ function accountRunCost(deps, run) {
|
|
|
11499
11577
|
const dir = findRunLogDir(deps.runLogsDir, deps.db.runLogKeys(run.id));
|
|
11500
11578
|
const files = dir ? unprocessedLogFiles(dir) : [];
|
|
11501
11579
|
if (!dir || files.length === 0) return;
|
|
11502
|
-
const
|
|
11503
|
-
const
|
|
11580
|
+
const pinnedRef = deps.modelRefFor?.(run) ?? null;
|
|
11581
|
+
const model = (pinnedRef ?? resolveRunModelRef(run, deps.swarmModels?.() ?? null)).model;
|
|
11582
|
+
const executor = deps.executor ?? executorForRun(run, deps.swarmModels?.() ?? null, process.env, pinnedRef);
|
|
11504
11583
|
const now = deps.now?.() ?? Date.now();
|
|
11505
11584
|
const starts = segmentStarts(dir);
|
|
11506
11585
|
let added = 0;
|
|
@@ -11614,13 +11693,67 @@ function abortOfExit(runLogsDir, runLogKeys, executor = resolveExecutor()) {
|
|
|
11614
11693
|
if (raw == null) return null;
|
|
11615
11694
|
return executor.parseResult(raw, null)?.aborted ?? null;
|
|
11616
11695
|
}
|
|
11617
|
-
function makeExitClassifier(runLogsDir, runLogKeys,
|
|
11618
|
-
|
|
11619
|
-
return (run) => classifyExit(runLogsDir, runLogKeys(run.id), exec);
|
|
11696
|
+
function makeExitClassifier(runLogsDir, runLogKeys, executorFor = () => resolveExecutor()) {
|
|
11697
|
+
return (run) => classifyExit(runLogsDir, runLogKeys(run.id), executorFor(run));
|
|
11620
11698
|
}
|
|
11621
|
-
function makeAbortReader(runLogsDir, runLogKeys,
|
|
11622
|
-
|
|
11623
|
-
|
|
11699
|
+
function makeAbortReader(runLogsDir, runLogKeys, executorFor = () => resolveExecutor()) {
|
|
11700
|
+
return (run) => abortOfExit(runLogsDir, runLogKeys(run.id), executorFor(run));
|
|
11701
|
+
}
|
|
11702
|
+
|
|
11703
|
+
// src/overmind.ts
|
|
11704
|
+
init_executors();
|
|
11705
|
+
init_claudeCode();
|
|
11706
|
+
init_swarmModels();
|
|
11707
|
+
|
|
11708
|
+
// src/executors/detect.ts
|
|
11709
|
+
init_executors();
|
|
11710
|
+
import { accessSync, constants } from "fs";
|
|
11711
|
+
import { delimiter, join as join20 } from "path";
|
|
11712
|
+
function binRunnable(bin, env) {
|
|
11713
|
+
if (!bin) return false;
|
|
11714
|
+
if (bin.includes("/") || bin.includes("\\")) {
|
|
11715
|
+
try {
|
|
11716
|
+
accessSync(bin, constants.X_OK);
|
|
11717
|
+
return true;
|
|
11718
|
+
} catch {
|
|
11719
|
+
return false;
|
|
11720
|
+
}
|
|
11721
|
+
}
|
|
11722
|
+
const exts = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
|
|
11723
|
+
for (const dir of (env.PATH ?? "").split(delimiter)) {
|
|
11724
|
+
if (!dir) continue;
|
|
11725
|
+
for (const ext of exts) {
|
|
11726
|
+
try {
|
|
11727
|
+
accessSync(join20(dir, bin + ext), constants.X_OK);
|
|
11728
|
+
return true;
|
|
11729
|
+
} catch {
|
|
11730
|
+
}
|
|
11731
|
+
}
|
|
11732
|
+
}
|
|
11733
|
+
return false;
|
|
11734
|
+
}
|
|
11735
|
+
function detectInstalledExecutors(env = process.env) {
|
|
11736
|
+
return Object.values(EXECUTORS).map((executor) => {
|
|
11737
|
+
let bin = "";
|
|
11738
|
+
try {
|
|
11739
|
+
bin = executor.resolveBin(env);
|
|
11740
|
+
} catch {
|
|
11741
|
+
bin = "";
|
|
11742
|
+
}
|
|
11743
|
+
return { id: executor.id, title: executor.title, bin, installed: binRunnable(bin, env) };
|
|
11744
|
+
});
|
|
11745
|
+
}
|
|
11746
|
+
function installedExecutorIds(env = process.env) {
|
|
11747
|
+
return detectInstalledExecutors(env).filter((e) => e.installed).map((e) => e.id);
|
|
11748
|
+
}
|
|
11749
|
+
var cachedAt = Number.NaN;
|
|
11750
|
+
var cachedIds = [];
|
|
11751
|
+
function cachedInstalledExecutorIds(now = Date.now(), ttlMs = 3e5, env = process.env) {
|
|
11752
|
+
if (!Number.isFinite(cachedAt) || now - cachedAt > ttlMs) {
|
|
11753
|
+
cachedAt = now;
|
|
11754
|
+
cachedIds = installedExecutorIds(env);
|
|
11755
|
+
}
|
|
11756
|
+
return cachedIds;
|
|
11624
11757
|
}
|
|
11625
11758
|
|
|
11626
11759
|
// src/orchestrator.ts
|
|
@@ -11629,19 +11762,19 @@ init_git();
|
|
|
11629
11762
|
|
|
11630
11763
|
// src/deps.ts
|
|
11631
11764
|
import { existsSync as existsSync14, readdirSync as readdirSync13, readFileSync as readFileSync18 } from "fs";
|
|
11632
|
-
import { join as
|
|
11765
|
+
import { join as join24 } from "path";
|
|
11633
11766
|
import { execa } from "execa";
|
|
11634
11767
|
|
|
11635
11768
|
// src/prepareLock.ts
|
|
11636
11769
|
import { createHash } from "crypto";
|
|
11637
11770
|
import { closeSync as closeSync4, openSync as openSync4, rmSync as rmSync2, statSync as statSync11, writeSync } from "fs";
|
|
11638
|
-
import { join as
|
|
11771
|
+
import { join as join22 } from "path";
|
|
11639
11772
|
import { tmpdir } from "os";
|
|
11640
11773
|
var WAIT_TIMEOUT_MS = 5 * 6e4;
|
|
11641
11774
|
var POLL_MS = 300;
|
|
11642
11775
|
var STALE_MS = 20 * 6e4;
|
|
11643
11776
|
function machineLockPath(key, dir = tmpdir()) {
|
|
11644
|
-
return
|
|
11777
|
+
return join22(dir, `swarm-prepare-${createHash("sha1").update(key).digest("hex").slice(0, 16)}.lock`);
|
|
11645
11778
|
}
|
|
11646
11779
|
function defaultTryCreate(path, content) {
|
|
11647
11780
|
try {
|
|
@@ -11703,7 +11836,7 @@ init_config();
|
|
|
11703
11836
|
import { createHash as createHash2 } from "crypto";
|
|
11704
11837
|
import { cp, link, lstat, mkdir as mkdir2, readdir, readlink, rename as rename2, rm as rm2, stat, symlink, utimes, writeFile as writeFile2 } from "fs/promises";
|
|
11705
11838
|
import { existsSync as existsSync13, readFileSync as readFileSync17 } from "fs";
|
|
11706
|
-
import { join as
|
|
11839
|
+
import { join as join23 } from "path";
|
|
11707
11840
|
function fingerprint(parts) {
|
|
11708
11841
|
const h = createHash2("sha256");
|
|
11709
11842
|
for (const p of parts) {
|
|
@@ -11717,8 +11850,8 @@ async function cloneTree(src, dst) {
|
|
|
11717
11850
|
const entries = await readdir(src, { withFileTypes: true });
|
|
11718
11851
|
await Promise.all(
|
|
11719
11852
|
entries.map(async (e) => {
|
|
11720
|
-
const from =
|
|
11721
|
-
const to =
|
|
11853
|
+
const from = join23(src, e.name);
|
|
11854
|
+
const to = join23(dst, e.name);
|
|
11722
11855
|
if (e.isDirectory()) return cloneTree(from, to);
|
|
11723
11856
|
if (e.isSymbolicLink()) {
|
|
11724
11857
|
return symlink(await readlink(from), to).catch(() => cp(from, to, { recursive: true, verbatimSymlinks: true }));
|
|
@@ -11731,11 +11864,11 @@ function cacheRoot(io) {
|
|
|
11731
11864
|
return io.root ?? ENV_CACHE_DIR;
|
|
11732
11865
|
}
|
|
11733
11866
|
function snapshotDir(io, key) {
|
|
11734
|
-
return
|
|
11867
|
+
return join23(cacheRoot(io), key);
|
|
11735
11868
|
}
|
|
11736
11869
|
function readManifest(dir) {
|
|
11737
11870
|
try {
|
|
11738
|
-
const parsed = JSON.parse(readFileSync17(
|
|
11871
|
+
const parsed = JSON.parse(readFileSync17(join23(dir, "manifest.json"), "utf8"));
|
|
11739
11872
|
if (!parsed || typeof parsed !== "object") return null;
|
|
11740
11873
|
const m = parsed;
|
|
11741
11874
|
if (typeof m.key !== "string" || !Array.isArray(m.dirs)) return null;
|
|
@@ -11748,7 +11881,7 @@ function readManifest(dir) {
|
|
|
11748
11881
|
}
|
|
11749
11882
|
function hasEnvSnapshot(key, io = {}) {
|
|
11750
11883
|
const exists = io.exists ?? existsSync13;
|
|
11751
|
-
return exists(
|
|
11884
|
+
return exists(join23(snapshotDir(io, key), "manifest.json"));
|
|
11752
11885
|
}
|
|
11753
11886
|
async function restoreEnvSnapshot(worktree, key, io = {}) {
|
|
11754
11887
|
const exists = io.exists ?? existsSync13;
|
|
@@ -11760,13 +11893,13 @@ async function restoreEnvSnapshot(worktree, key, io = {}) {
|
|
|
11760
11893
|
try {
|
|
11761
11894
|
for (let i = 0; i < manifest.dirs.length; i++) {
|
|
11762
11895
|
const rel = manifest.dirs[i] ?? "";
|
|
11763
|
-
const target =
|
|
11896
|
+
const target = join23(worktree, rel);
|
|
11764
11897
|
if (exists(target)) continue;
|
|
11765
|
-
await clone(
|
|
11898
|
+
await clone(join23(dir, "trees", String(i)), target);
|
|
11766
11899
|
made.push(rel);
|
|
11767
11900
|
}
|
|
11768
11901
|
} catch {
|
|
11769
|
-
for (const rel of made) await rm2(
|
|
11902
|
+
for (const rel of made) await rm2(join23(worktree, rel), { recursive: true, force: true }).catch(() => {
|
|
11770
11903
|
});
|
|
11771
11904
|
return null;
|
|
11772
11905
|
}
|
|
@@ -11782,16 +11915,16 @@ async function saveEnvSnapshot(worktree, key, dirs, io = {}) {
|
|
|
11782
11915
|
const exists = io.exists ?? existsSync13;
|
|
11783
11916
|
const clone = io.clone ?? cloneTree;
|
|
11784
11917
|
if (hasEnvSnapshot(key, io)) return true;
|
|
11785
|
-
const present = dirs.filter((rel) => exists(
|
|
11918
|
+
const present = dirs.filter((rel) => exists(join23(worktree, rel)));
|
|
11786
11919
|
if (!present.length) return false;
|
|
11787
11920
|
const root = cacheRoot(io);
|
|
11788
|
-
const tmp =
|
|
11921
|
+
const tmp = join23(root, `.tmp-${key}-${process.pid}`);
|
|
11789
11922
|
try {
|
|
11790
11923
|
await rm2(tmp, { recursive: true, force: true });
|
|
11791
|
-
await mkdir2(
|
|
11792
|
-
for (let i = 0; i < present.length; i++) await clone(
|
|
11924
|
+
await mkdir2(join23(tmp, "trees"), { recursive: true });
|
|
11925
|
+
for (let i = 0; i < present.length; i++) await clone(join23(worktree, present[i] ?? ""), join23(tmp, "trees", String(i)));
|
|
11793
11926
|
const manifest = { key, dirs: present, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
11794
|
-
await writeFile2(
|
|
11927
|
+
await writeFile2(join23(tmp, "manifest.json"), JSON.stringify(manifest, null, 2));
|
|
11795
11928
|
await rename2(tmp, snapshotDir(io, key));
|
|
11796
11929
|
return true;
|
|
11797
11930
|
} catch {
|
|
@@ -11811,13 +11944,13 @@ async function pruneEnvSnapshots(io = {}) {
|
|
|
11811
11944
|
}
|
|
11812
11945
|
const withTime = [];
|
|
11813
11946
|
for (const name of names) {
|
|
11814
|
-
const s = await stat(
|
|
11947
|
+
const s = await stat(join23(root, name)).catch(() => null);
|
|
11815
11948
|
if (s) withTime.push({ name, mtime: s.mtimeMs });
|
|
11816
11949
|
}
|
|
11817
11950
|
withTime.sort((a, b) => b.mtime - a.mtime);
|
|
11818
11951
|
let removed = 0;
|
|
11819
11952
|
for (const { name } of withTime.slice(Math.max(0, keep))) {
|
|
11820
|
-
await rm2(
|
|
11953
|
+
await rm2(join23(root, name), { recursive: true, force: true }).catch(() => {
|
|
11821
11954
|
});
|
|
11822
11955
|
removed++;
|
|
11823
11956
|
}
|
|
@@ -11833,9 +11966,9 @@ async function sweepEnvCacheTemp(io = {}) {
|
|
|
11833
11966
|
}
|
|
11834
11967
|
let removed = 0;
|
|
11835
11968
|
for (const name of names) {
|
|
11836
|
-
const s = await lstat(
|
|
11969
|
+
const s = await lstat(join23(root, name)).catch(() => null);
|
|
11837
11970
|
if (!s || Date.now() - s.mtimeMs < 60 * 6e4) continue;
|
|
11838
|
-
await rm2(
|
|
11971
|
+
await rm2(join23(root, name), { recursive: true, force: true }).catch(() => {
|
|
11839
11972
|
});
|
|
11840
11973
|
removed++;
|
|
11841
11974
|
}
|
|
@@ -11904,19 +12037,19 @@ function overridePlan(override) {
|
|
|
11904
12037
|
function detectPackageManager(worktree, io = {}) {
|
|
11905
12038
|
const exists = io.exists ?? existsSync14;
|
|
11906
12039
|
const readText = io.readText ?? safeReadText;
|
|
11907
|
-
const hasPkgJson = exists(
|
|
12040
|
+
const hasPkgJson = exists(join24(worktree, "package.json"));
|
|
11908
12041
|
if (!hasPkgJson) {
|
|
11909
|
-
if (exists(
|
|
12042
|
+
if (exists(join24(worktree, "deno.json")) || exists(join24(worktree, "deno.jsonc")) || exists(join24(worktree, "deno.lock"))) return "deno";
|
|
11910
12043
|
return null;
|
|
11911
12044
|
}
|
|
11912
|
-
const pkg = readJson(readText,
|
|
12045
|
+
const pkg = readJson(readText, join24(worktree, "package.json"));
|
|
11913
12046
|
const declared = typeof pkg?.packageManager === "string" ? pkg.packageManager.split("@")[0] : void 0;
|
|
11914
12047
|
if (declared === "pnpm" || declared === "bun" || declared === "npm" || declared === "yarn") return declared;
|
|
11915
|
-
if (exists(
|
|
11916
|
-
if (exists(
|
|
11917
|
-
if (exists(
|
|
11918
|
-
if (exists(
|
|
11919
|
-
if (exists(
|
|
12048
|
+
if (exists(join24(worktree, "bun.lock")) || exists(join24(worktree, "bun.lockb"))) return "bun";
|
|
12049
|
+
if (exists(join24(worktree, "pnpm-lock.yaml"))) return "pnpm";
|
|
12050
|
+
if (exists(join24(worktree, "yarn.lock"))) return "yarn";
|
|
12051
|
+
if (exists(join24(worktree, "package-lock.json"))) return "npm";
|
|
12052
|
+
if (exists(join24(worktree, "deno.lock"))) return "deno";
|
|
11920
12053
|
return "npm";
|
|
11921
12054
|
}
|
|
11922
12055
|
function jsInstallCommand(pm) {
|
|
@@ -11980,7 +12113,7 @@ function detectPlan(worktree, io) {
|
|
|
11980
12113
|
// node_modules на месте — установку пропускаем (быстрый путь переиспользованного worktree).
|
|
11981
12114
|
// У deno каталога может не быть вовсе (кэш в DENO_DIR) — тогда `deno install` отработает
|
|
11982
12115
|
// заново, но он дешёвый: тянет из того же общего кэша.
|
|
11983
|
-
{ label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists:
|
|
12116
|
+
{ label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists: join24(worktree, "node_modules"), source: "detect" }
|
|
11984
12117
|
];
|
|
11985
12118
|
for (const target of workspaceBuildTargets(worktree, io)) {
|
|
11986
12119
|
commands.push({ label: `\u0441\u0431\u043E\u0440\u043A\u0430 ${target.name}`, run: jsRunScript(pm, "build"), cwd: target.dir, source: "detect" });
|
|
@@ -11989,12 +12122,12 @@ function detectPlan(worktree, io) {
|
|
|
11989
12122
|
return { source: "detect", ecosystem: `node/${pm}`, commands };
|
|
11990
12123
|
}
|
|
11991
12124
|
for (const marker of ECOSYSTEM_MARKERS) {
|
|
11992
|
-
const hit = marker.files.some((f) => exists(
|
|
12125
|
+
const hit = marker.files.some((f) => exists(join24(worktree, f))) || marker.ext !== void 0 && listFiles(worktree).some((n) => n.endsWith(marker.ext));
|
|
11993
12126
|
if (!hit) continue;
|
|
11994
12127
|
let ecosystem = marker.ecosystem;
|
|
11995
12128
|
let command = marker.command;
|
|
11996
12129
|
for (const refine of marker.refine ?? []) {
|
|
11997
|
-
if (!exists(
|
|
12130
|
+
if (!exists(join24(worktree, refine.file))) continue;
|
|
11998
12131
|
ecosystem = refine.ecosystem;
|
|
11999
12132
|
command = refine.command;
|
|
12000
12133
|
break;
|
|
@@ -12051,12 +12184,12 @@ var CI_INSTALL_PATTERNS = [
|
|
|
12051
12184
|
function ciWorkflowPlan(worktree, io) {
|
|
12052
12185
|
const readText = io.readText ?? safeReadText;
|
|
12053
12186
|
const listFiles = io.listFiles ?? safeListFiles;
|
|
12054
|
-
const dir =
|
|
12187
|
+
const dir = join24(worktree, ".github", "workflows");
|
|
12055
12188
|
const files = listFiles(dir).filter((n) => n.endsWith(".yml") || n.endsWith(".yaml"));
|
|
12056
12189
|
const seen = /* @__PURE__ */ new Set();
|
|
12057
12190
|
const commands = [];
|
|
12058
12191
|
for (const name of files.sort()) {
|
|
12059
|
-
const yaml = readText(
|
|
12192
|
+
const yaml = readText(join24(dir, name));
|
|
12060
12193
|
if (yaml === null) continue;
|
|
12061
12194
|
for (const raw of workflowRunLines(yaml)) {
|
|
12062
12195
|
const line = raw.replace(/\s+/g, " ").trim();
|
|
@@ -12071,7 +12204,7 @@ function ciWorkflowPlan(worktree, io) {
|
|
|
12071
12204
|
return commands.length ? { source: "ci-workflow", ecosystem: "ci-\u043A\u043E\u043D\u0444\u0438\u0433", commands } : null;
|
|
12072
12205
|
}
|
|
12073
12206
|
function workspaceGlobs(worktree, readText) {
|
|
12074
|
-
const yaml = readText(
|
|
12207
|
+
const yaml = readText(join24(worktree, "pnpm-workspace.yaml"));
|
|
12075
12208
|
if (yaml !== null) {
|
|
12076
12209
|
const globs = [];
|
|
12077
12210
|
let inPackages = false;
|
|
@@ -12091,7 +12224,7 @@ function workspaceGlobs(worktree, readText) {
|
|
|
12091
12224
|
}
|
|
12092
12225
|
if (globs.length) return globs;
|
|
12093
12226
|
}
|
|
12094
|
-
const pkg = readJson(readText,
|
|
12227
|
+
const pkg = readJson(readText, join24(worktree, "package.json"));
|
|
12095
12228
|
const ws = pkg?.workspaces;
|
|
12096
12229
|
const list = Array.isArray(ws) ? ws : Array.isArray(ws?.packages) ? ws.packages : [];
|
|
12097
12230
|
return list.filter((g) => typeof g === "string");
|
|
@@ -12102,14 +12235,14 @@ function workspaceDirs(worktree, globs, io) {
|
|
|
12102
12235
|
if (glob.startsWith("!")) continue;
|
|
12103
12236
|
const star = glob.indexOf("*");
|
|
12104
12237
|
if (star === -1) {
|
|
12105
|
-
const dir =
|
|
12106
|
-
if (io.exists(
|
|
12238
|
+
const dir = join24(worktree, glob);
|
|
12239
|
+
if (io.exists(join24(dir, "package.json"))) dirs.add(dir);
|
|
12107
12240
|
continue;
|
|
12108
12241
|
}
|
|
12109
|
-
const base3 =
|
|
12242
|
+
const base3 = join24(worktree, glob.slice(0, star).replace(/\/$/, ""));
|
|
12110
12243
|
for (const name of io.listDirs(base3)) {
|
|
12111
|
-
const dir =
|
|
12112
|
-
if (io.exists(
|
|
12244
|
+
const dir = join24(base3, name);
|
|
12245
|
+
if (io.exists(join24(dir, "package.json"))) dirs.add(dir);
|
|
12113
12246
|
}
|
|
12114
12247
|
}
|
|
12115
12248
|
return [...dirs];
|
|
@@ -12137,11 +12270,11 @@ function workspaceBuildTargets(worktree, io = {}) {
|
|
|
12137
12270
|
if (!globs.length) return [];
|
|
12138
12271
|
const pkgs = [];
|
|
12139
12272
|
for (const dir of workspaceDirs(worktree, globs, { exists, listDirs })) {
|
|
12140
|
-
const pkg = readJson(readText,
|
|
12273
|
+
const pkg = readJson(readText, join24(dir, "package.json"));
|
|
12141
12274
|
if (!pkg || typeof pkg.name !== "string") continue;
|
|
12142
12275
|
const scripts = pkg.scripts ?? {};
|
|
12143
12276
|
const entries = entryPaths(pkg);
|
|
12144
|
-
const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(
|
|
12277
|
+
const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(join24(dir, e)));
|
|
12145
12278
|
const deps = Object.keys({ ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} });
|
|
12146
12279
|
pkgs.push({ name: pkg.name, dir, deps, needsBuild });
|
|
12147
12280
|
}
|
|
@@ -12169,7 +12302,7 @@ function devDatabaseUrl(worktree, io = {}) {
|
|
|
12169
12302
|
const fromEnv = (io.env ?? process.env).DATABASE_URL;
|
|
12170
12303
|
if (fromEnv && fromEnv.trim()) return fromEnv.trim();
|
|
12171
12304
|
for (const file of [".env", ".env.example"]) {
|
|
12172
|
-
const raw = readText(
|
|
12305
|
+
const raw = readText(join24(worktree, file));
|
|
12173
12306
|
if (raw === null) continue;
|
|
12174
12307
|
const m = /^\s*(?:export\s+)?DATABASE_URL\s*=\s*(.*)$/m.exec(raw);
|
|
12175
12308
|
const value = (m?.[1] ?? "").trim().replace(/^['"]|['"]$/g, "");
|
|
@@ -12189,7 +12322,7 @@ function prismaPackages(worktree, io) {
|
|
|
12189
12322
|
const readText = io.readText ?? safeReadText;
|
|
12190
12323
|
const listDirs = io.listDirs ?? safeListDirs;
|
|
12191
12324
|
const dirs = [worktree, ...workspaceDirs(worktree, workspaceGlobs(worktree, readText), { exists, listDirs })];
|
|
12192
|
-
return dirs.filter((dir) => exists(
|
|
12325
|
+
return dirs.filter((dir) => exists(join24(dir, "prisma", "schema.prisma")) && listDirs(join24(dir, "prisma", "migrations")).length > 0);
|
|
12193
12326
|
}
|
|
12194
12327
|
function jsExecBin(pm, command) {
|
|
12195
12328
|
switch (pm) {
|
|
@@ -12226,18 +12359,18 @@ function envCachePlan(worktree, io = {}) {
|
|
|
12226
12359
|
if (!pm) return null;
|
|
12227
12360
|
const parts = [`pm:${pm}`];
|
|
12228
12361
|
for (const name of ENV_DEFINING_FILES) {
|
|
12229
|
-
const raw = readText(
|
|
12362
|
+
const raw = readText(join24(worktree, name));
|
|
12230
12363
|
if (raw !== null) parts.push(`${name}
|
|
12231
12364
|
${raw}`);
|
|
12232
12365
|
}
|
|
12233
12366
|
const pkgDirs = workspaceDirs(worktree, workspaceGlobs(worktree, readText), { exists, listDirs });
|
|
12234
12367
|
const rels = pkgDirs.map((dir) => dir.slice(worktree.length + 1)).sort();
|
|
12235
12368
|
for (const rel of ["", ...rels]) {
|
|
12236
|
-
const raw = readText(
|
|
12369
|
+
const raw = readText(join24(worktree, rel, "package.json"));
|
|
12237
12370
|
if (raw !== null) parts.push(`${rel}/package.json
|
|
12238
12371
|
${raw}`);
|
|
12239
12372
|
}
|
|
12240
|
-
return { key: fingerprint(parts), dirs: ["node_modules", ...rels.map((rel) =>
|
|
12373
|
+
return { key: fingerprint(parts), dirs: ["node_modules", ...rels.map((rel) => join24(rel, "node_modules"))] };
|
|
12241
12374
|
}
|
|
12242
12375
|
function planPrepare(worktree, io = {}) {
|
|
12243
12376
|
const plan = overridePlan(io.override) ?? detectPlan(worktree, io) ?? ciWorkflowPlan(worktree, io);
|
|
@@ -12262,7 +12395,7 @@ async function ensureWorktreeDeps(worktree, io = {}) {
|
|
|
12262
12395
|
restored: [],
|
|
12263
12396
|
cached: false
|
|
12264
12397
|
};
|
|
12265
|
-
if (cachePlan && !exists(
|
|
12398
|
+
if (cachePlan && !exists(join24(worktree, "node_modules"))) {
|
|
12266
12399
|
report.restored = await restoreEnvSnapshot(worktree, cachePlan.key, cacheIO) ?? [];
|
|
12267
12400
|
}
|
|
12268
12401
|
const plan = planPrepare(worktree, io);
|
|
@@ -12593,7 +12726,7 @@ function parkReasonUnretriable(reason) {
|
|
|
12593
12726
|
// package.json
|
|
12594
12727
|
var package_default = {
|
|
12595
12728
|
name: "@taskless-app/swarm",
|
|
12596
|
-
version: "0.6.
|
|
12729
|
+
version: "0.6.59",
|
|
12597
12730
|
type: "module",
|
|
12598
12731
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
12599
12732
|
keywords: [
|
|
@@ -12644,7 +12777,7 @@ var package_default = {
|
|
|
12644
12777
|
execa: "^10.0.0",
|
|
12645
12778
|
zod: "3.25.76"
|
|
12646
12779
|
},
|
|
12647
|
-
gitHead: "
|
|
12780
|
+
gitHead: "13cdd65f2929d86153c0a52715c0d9891f8a1e4b"
|
|
12648
12781
|
};
|
|
12649
12782
|
|
|
12650
12783
|
// src/version.ts
|
|
@@ -13433,7 +13566,7 @@ init_git();
|
|
|
13433
13566
|
|
|
13434
13567
|
// src/diff.ts
|
|
13435
13568
|
import { statSync as statSync12 } from "fs";
|
|
13436
|
-
import { join as
|
|
13569
|
+
import { join as join29 } from "path";
|
|
13437
13570
|
import { $ as $4 } from "execa";
|
|
13438
13571
|
var MAX_FILES = 200;
|
|
13439
13572
|
var MAX_LINES_PER_FILE = 1500;
|
|
@@ -13601,7 +13734,7 @@ async function collectRunDiff(worktree, opts = {}) {
|
|
|
13601
13734
|
files.push(pf);
|
|
13602
13735
|
}
|
|
13603
13736
|
}
|
|
13604
|
-
files.sort((a, b) => mtime(
|
|
13737
|
+
files.sort((a, b) => mtime(join29(worktree, b.path)) - mtime(join29(worktree, a.path)));
|
|
13605
13738
|
const truncatedFiles = files.length > MAX_FILES;
|
|
13606
13739
|
if (truncatedFiles) files = files.slice(0, MAX_FILES);
|
|
13607
13740
|
const added = files.reduce((s, f) => s + f.added, 0);
|
|
@@ -15415,14 +15548,14 @@ function makeMaxDronesReader(opts) {
|
|
|
15415
15548
|
const ttl = opts.ttlMs ?? 1e4;
|
|
15416
15549
|
const now = opts.now ?? Date.now;
|
|
15417
15550
|
let cached = null;
|
|
15418
|
-
let
|
|
15551
|
+
let cachedAt2 = -Infinity;
|
|
15419
15552
|
return async () => {
|
|
15420
15553
|
const t = now();
|
|
15421
|
-
if (cached != null && t -
|
|
15554
|
+
if (cached != null && t - cachedAt2 < ttl) return cached;
|
|
15422
15555
|
const live = await opts.fetch().catch(() => null);
|
|
15423
15556
|
if (live != null && Number.isFinite(live) && live > 0) {
|
|
15424
15557
|
cached = Math.trunc(live);
|
|
15425
|
-
|
|
15558
|
+
cachedAt2 = t;
|
|
15426
15559
|
return cached;
|
|
15427
15560
|
}
|
|
15428
15561
|
return opts.fallback;
|
|
@@ -15855,20 +15988,20 @@ async function prodClearMergeIntent(runId, deps = {}) {
|
|
|
15855
15988
|
|
|
15856
15989
|
// src/envGate.ts
|
|
15857
15990
|
import { existsSync as existsSync17 } from "fs";
|
|
15858
|
-
import { join as
|
|
15991
|
+
import { join as join31 } from "path";
|
|
15859
15992
|
import { createConnection } from "net";
|
|
15860
15993
|
import { execa as execa2 } from "execa";
|
|
15861
15994
|
|
|
15862
15995
|
// src/platform.ts
|
|
15863
15996
|
import { existsSync as existsSync16, readFileSync as readFileSync22 } from "fs";
|
|
15864
|
-
import { delimiter, join as
|
|
15997
|
+
import { delimiter as delimiter2, join as join30 } from "path";
|
|
15865
15998
|
function resolveCommandPath(name, env = process.env, platform = process.platform) {
|
|
15866
15999
|
if (name.includes("/") || name.includes("\\")) return existsSync16(name) ? name : null;
|
|
15867
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
16000
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(delimiter2).filter(Boolean);
|
|
15868
16001
|
const exts = platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean) : [""];
|
|
15869
16002
|
for (const dir of dirs) {
|
|
15870
16003
|
for (const ext of exts) {
|
|
15871
|
-
const candidate =
|
|
16004
|
+
const candidate = join30(dir, name + ext);
|
|
15872
16005
|
if (existsSync16(candidate)) return candidate;
|
|
15873
16006
|
}
|
|
15874
16007
|
}
|
|
@@ -15953,7 +16086,7 @@ var COMPOSE_MARKERS = [
|
|
|
15953
16086
|
];
|
|
15954
16087
|
function detectRequirements(repo, io = {}) {
|
|
15955
16088
|
const exists = io.exists ?? existsSync17;
|
|
15956
|
-
const marker = COMPOSE_MARKERS.find((f) => exists(
|
|
16089
|
+
const marker = COMPOSE_MARKERS.find((f) => exists(join31(repo, f)));
|
|
15957
16090
|
return marker ? [{ kind: "docker", raw: `docker (\u0434\u0435\u0442\u0435\u043A\u0442: ${marker})` }] : [];
|
|
15958
16091
|
}
|
|
15959
16092
|
function resolveRequirements(repo, declared, io = {}) {
|
|
@@ -16104,8 +16237,9 @@ function stormFacts(db) {
|
|
|
16104
16237
|
if (!db.stormActive()) return null;
|
|
16105
16238
|
const since = db.currentStormStartedAt();
|
|
16106
16239
|
const recs = since ? db.stormProbeRecordsSince(since) : [];
|
|
16240
|
+
const last = lastProbeReason(recs);
|
|
16107
16241
|
return {
|
|
16108
|
-
reason:
|
|
16242
|
+
reason: last === "limit" ? "limit" : last === "auth" ? "auth" : "provider",
|
|
16109
16243
|
hint: recs.length ? recs[recs.length - 1].resetHint ?? null : null
|
|
16110
16244
|
};
|
|
16111
16245
|
}
|
|
@@ -16731,6 +16865,13 @@ function stormBanner(storm) {
|
|
|
16731
16865
|
desc: `\u0414\u0440\u043E\u043D\u044B \u043F\u043E\u0435\u0434\u0443\u0442 \u043F\u043E\u0441\u043B\u0435 \u0440\u0435\u0437\u0435\u0442\u0430 \u043B\u0438\u043C\u0438\u0442\u0430${storm.resetHint ? ` \xB7 ${storm.resetHint}` : ""}. Anthropic \u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u2014 \u044D\u0442\u043E \u043B\u0438\u043C\u0438\u0442 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438, \u043D\u0435 \u0441\u0431\u043E\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430.`
|
|
16732
16866
|
};
|
|
16733
16867
|
}
|
|
16868
|
+
if (storm.reason === "auth") {
|
|
16869
|
+
return {
|
|
16870
|
+
ico: "\u{1F511}",
|
|
16871
|
+
title: "\u0414\u043E\u0441\u0442\u0443\u043F \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F \u043E\u0442\u0432\u0435\u0440\u0433\u043D\u0443\u0442 \u2014 \u0440\u043E\u0439 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435",
|
|
16872
|
+
desc: "\u041F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 \u0436\u0438\u0432, \u043D\u043E \u043F\u0443\u0441\u043A\u0430\u0435\u0442 \u043D\u0435 \u043D\u0430\u0441: \u043A\u043B\u044E\u0447, \u0430\u043A\u043A\u0430\u0443\u043D\u0442 \u0438\u043B\u0438 \u0440\u0435\u0433\u0438\u043E\u043D (403 \u0431\u0435\u0437 VPN). \u0421\u0430\u043C\u043E \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442\u0441\u044F \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044C \u0432\u0445\u043E\u0434 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F (`swarm doctor`)."
|
|
16873
|
+
};
|
|
16874
|
+
}
|
|
16734
16875
|
const frozen = storm.dead ? `${storm.dead} \u0432 \u043A\u043E\u043A\u043E\u043D\u0435` : "\u043D\u043E\u0432\u044B\u0445 \u0434\u0440\u043E\u043D\u043E\u0432 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u043C";
|
|
16735
16876
|
return {
|
|
16736
16877
|
ico: "\u26A1",
|
|
@@ -17288,6 +17429,10 @@ function paintStorm(){ var b=document.getElementById('storm-banner'); if(!b) ret
|
|
|
17288
17429
|
b.innerHTML='<span class="sb-ico">\u23F3</span><div class="sb-body"><div class="sb-t">\u041B\u0438\u043C\u0438\u0442 Claude \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u0440\u043E\u0439 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435</div>'+
|
|
17289
17430
|
'<div class="sb-d">\u0414\u0440\u043E\u043D\u044B \u043F\u043E\u0435\u0434\u0443\u0442 \u043F\u043E\u0441\u043B\u0435 \u0440\u0435\u0437\u0435\u0442\u0430 \u043B\u0438\u043C\u0438\u0442\u0430'+reset+E(since)+'. Anthropic \u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u2014 \u044D\u0442\u043E \u043B\u0438\u043C\u0438\u0442 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438, \u043D\u0435 \u0441\u0431\u043E\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430.</div></div>';
|
|
17290
17431
|
b.hidden=false; return; }
|
|
17432
|
+
if(st.reason==='auth'){
|
|
17433
|
+
b.innerHTML='<span class="sb-ico">\u{1F511}</span><div class="sb-body"><div class="sb-t">\u0414\u043E\u0441\u0442\u0443\u043F \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F \u043E\u0442\u0432\u0435\u0440\u0433\u043D\u0443\u0442 \u2014 \u0440\u043E\u0439 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435</div>'+
|
|
17434
|
+
'<div class="sb-d">\u041F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 \u0436\u0438\u0432, \u043D\u043E \u043F\u0443\u0441\u043A\u0430\u0435\u0442 \u043D\u0435 \u043D\u0430\u0441: \u043A\u043B\u044E\u0447, \u0430\u043A\u043A\u0430\u0443\u043D\u0442 \u0438\u043B\u0438 \u0440\u0435\u0433\u0438\u043E\u043D'+E(since)+'. \u0421\u0430\u043C\u043E \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442\u0441\u044F \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044C \u0432\u0445\u043E\u0434 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F (swarm doctor).</div></div>';
|
|
17435
|
+
b.hidden=false; return; }
|
|
17291
17436
|
var frozen = st.dead ? st.dead+' '+plural(st.dead,DRONEW)+' \u0432 \u043A\u043E\u043A\u043E\u043D\u0435' : '\u043D\u043E\u0432\u044B\u0445 \u0434\u0440\u043E\u043D\u043E\u0432 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u043C';
|
|
17292
17437
|
b.innerHTML='<span class="sb-ico">\u26A1</span><div class="sb-body"><div class="sb-t">\u041F\u0441\u0438\u043E\u043D\u043D\u0430\u044F \u0441\u0432\u044F\u0437\u044C \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u0430 \u2014 Anthropic \u0448\u0442\u043E\u0440\u043C\u0438\u0442</div>'+
|
|
17293
17438
|
'<div class="sb-d">\u0420\u043E\u0439 \u0437\u0430\u043C\u0435\u0440, \u0434\u0440\u043E\u043D\u044B \u0432 \u043A\u043E\u043A\u043E\u043D\u0430\u0445 \u2014 '+E(frozen)+', \u043E\u0447\u0435\u0440\u0435\u0434\u044C \u043A\u043E\u043F\u0438\u0442\u0441\u044F'+E(since)+'. \u041F\u043E\u0434\u043D\u0438\u043C\u0435\u0442\u0441\u044F \u0441\u0430\u043C, \u043A\u0430\u043A \u0441\u0432\u044F\u0437\u044C \u0432\u0435\u0440\u043D\u0451\u0442\u0441\u044F.</div></div>';
|
|
@@ -17697,9 +17842,9 @@ function closeWebServer(server) {
|
|
|
17697
17842
|
// src/hostCommands.ts
|
|
17698
17843
|
init_src();
|
|
17699
17844
|
import { readFileSync as readFileSync23, renameSync as renameSync10, writeFileSync as writeFileSync15 } from "fs";
|
|
17700
|
-
import { join as
|
|
17845
|
+
import { join as join32 } from "path";
|
|
17701
17846
|
function hostCommandStatePath(home) {
|
|
17702
|
-
return
|
|
17847
|
+
return join32(home, "host-command.json");
|
|
17703
17848
|
}
|
|
17704
17849
|
function readState(path) {
|
|
17705
17850
|
try {
|
|
@@ -17874,7 +18019,7 @@ async function doRestart(cmd, opts, note) {
|
|
|
17874
18019
|
// src/selfUpdate.ts
|
|
17875
18020
|
init_git();
|
|
17876
18021
|
import { existsSync as existsSync18 } from "fs";
|
|
17877
|
-
import { join as
|
|
18022
|
+
import { join as join33 } from "path";
|
|
17878
18023
|
import { $ as $5 } from "execa";
|
|
17879
18024
|
var DAEMON_PACKAGE = "@taskless-app/swarm";
|
|
17880
18025
|
var defaultRun = async (cmd, args, cwd) => {
|
|
@@ -17916,7 +18061,7 @@ async function runSelfUpdate(io) {
|
|
|
17916
18061
|
};
|
|
17917
18062
|
}
|
|
17918
18063
|
await io.onStep("install", "\u0441\u0442\u0430\u0432\u043B\u044E \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438");
|
|
17919
|
-
if (!exists(
|
|
18064
|
+
if (!exists(join33(repo, "pnpm-lock.yaml"))) {
|
|
17920
18065
|
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" };
|
|
17921
18066
|
}
|
|
17922
18067
|
const installed2 = await run("pnpm", ["install", "--frozen-lockfile"], repo);
|
|
@@ -18036,7 +18181,10 @@ function executorHealthy(opts = {}) {
|
|
|
18036
18181
|
}
|
|
18037
18182
|
resolve5(r);
|
|
18038
18183
|
};
|
|
18039
|
-
const child = spawn3(bin, [...executor.probe.args], {
|
|
18184
|
+
const child = spawn3(bin, [...executor.probe.args], {
|
|
18185
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
18186
|
+
...opts.env ? { env: { ...process.env, ...opts.env } } : {}
|
|
18187
|
+
});
|
|
18040
18188
|
child.stdout?.on("data", (d) => {
|
|
18041
18189
|
out += d.toString();
|
|
18042
18190
|
});
|
|
@@ -18045,11 +18193,14 @@ function executorHealthy(opts = {}) {
|
|
|
18045
18193
|
});
|
|
18046
18194
|
const timer = setTimeout(() => finish({ ok: false, reason: "provider", resetHint: null }), timeoutMs);
|
|
18047
18195
|
child.on("error", () => finish({ ok: false, reason: "exec", resetHint: null }));
|
|
18048
|
-
child.on("
|
|
18196
|
+
child.on("close", (code) => {
|
|
18049
18197
|
if (code === 0) return finish({ ok: true, reason: "ok", resetHint: null });
|
|
18050
18198
|
if (executor.probe.limitPattern.test(out)) {
|
|
18051
18199
|
return finish({ ok: false, reason: "limit", resetHint: out.match(RESET_RE)?.[0]?.trim() ?? null });
|
|
18052
18200
|
}
|
|
18201
|
+
if (executor.probe.authPattern?.test(out)) {
|
|
18202
|
+
return finish({ ok: false, reason: "auth", resetHint: null });
|
|
18203
|
+
}
|
|
18053
18204
|
finish({ ok: false, reason: "provider", resetHint: null });
|
|
18054
18205
|
});
|
|
18055
18206
|
});
|
|
@@ -18076,14 +18227,14 @@ async function fetchExternalStorm(url, opts = {}) {
|
|
|
18076
18227
|
|
|
18077
18228
|
// src/anomalyState.ts
|
|
18078
18229
|
import { existsSync as existsSync19, readFileSync as readFileSync24, renameSync as renameSync11, writeFileSync as writeFileSync16 } from "fs";
|
|
18079
|
-
import { join as
|
|
18230
|
+
import { join as join34 } from "path";
|
|
18080
18231
|
var KEY_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
18081
18232
|
var MAX_RESOLVED = 200;
|
|
18082
18233
|
function emptyState() {
|
|
18083
18234
|
return { since: {}, lastFiredAt: {}, fires: {}, resolved: [], stopQuiet: null };
|
|
18084
18235
|
}
|
|
18085
18236
|
function anomalyStatePath(home) {
|
|
18086
|
-
return
|
|
18237
|
+
return join34(home, "anomaly-state.json");
|
|
18087
18238
|
}
|
|
18088
18239
|
function loadAnomalyState(home, now = Date.now(), io = {}) {
|
|
18089
18240
|
const exists = io.exists ?? existsSync19;
|
|
@@ -18282,7 +18433,7 @@ function decideFreshAction(db, wt) {
|
|
|
18282
18433
|
for (const r of stalled) {
|
|
18283
18434
|
if (findEscalation(db.events(r.id).map((e) => e.text ?? ""), r, "author")) continue;
|
|
18284
18435
|
const path = r.worktree_path;
|
|
18285
|
-
const recoverable = !!path && wt.exists(path) && wt.hasTranscript(
|
|
18436
|
+
const recoverable = !!path && wt.exists(path) && wt.hasTranscript(r);
|
|
18286
18437
|
if (recoverable) continue;
|
|
18287
18438
|
return {
|
|
18288
18439
|
type: "fresh",
|
|
@@ -18561,7 +18712,7 @@ function withManualAreas(history, areas) {
|
|
|
18561
18712
|
// src/overmind.ts
|
|
18562
18713
|
var ANOMALY_RESOLVE_BATCH = 50;
|
|
18563
18714
|
var THIS_FILE = fileURLToPath3(import.meta.url);
|
|
18564
|
-
var executorHasSession = (
|
|
18715
|
+
var executorHasSession = (run) => !!run.worktree_path && EXECUTOR.session.exists(run.worktree_path);
|
|
18565
18716
|
var OVERMIND_ENTRY = THIS_FILE;
|
|
18566
18717
|
var STALL_MS = Number(process.env.SWARM_STALL_MS) || 10 * 60 * 1e3;
|
|
18567
18718
|
var STALL_FLOOR_MS = Number(process.env.SWARM_STALL_FLOOR_MS) || 30 * 60 * 1e3;
|
|
@@ -18670,7 +18821,7 @@ async function livenessMs(deps, run, now, liveChildPpids) {
|
|
|
18670
18821
|
if (commitMs != null) ms = Math.max(ms, commitMs);
|
|
18671
18822
|
}
|
|
18672
18823
|
if (deps.transcriptMtimeMs) {
|
|
18673
|
-
const transcriptMs = deps.transcriptMtimeMs(run
|
|
18824
|
+
const transcriptMs = deps.transcriptMtimeMs(run);
|
|
18674
18825
|
if (transcriptMs != null) ms = Math.max(ms, transcriptMs);
|
|
18675
18826
|
}
|
|
18676
18827
|
return ms;
|
|
@@ -19362,7 +19513,7 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
|
|
|
19362
19513
|
}
|
|
19363
19514
|
return parts.join("\n");
|
|
19364
19515
|
}
|
|
19365
|
-
var HERO_CHARTER_PATH =
|
|
19516
|
+
var HERO_CHARTER_PATH = join35(dirname5(THIS_FILE), "..", "docs", "HERO.md");
|
|
19366
19517
|
function buildHeroPrompt(task, run, taskContext, attachmentsBlock) {
|
|
19367
19518
|
const name = run.name || `#${run.id}`;
|
|
19368
19519
|
const charter = readFileSync25(HERO_CHARTER_PATH, "utf8");
|
|
@@ -19422,7 +19573,7 @@ function ciTrustLine(prUrl, verifyMode) {
|
|
|
19422
19573
|
}
|
|
19423
19574
|
return `\u041D\u0435 \u043F\u0435\u0440\u0435\u0433\u043E\u043D\u044F\u0439 \u0442\u0435\u0441\u0442-\u0441\u044C\u044E\u0442/typecheck \u0437\u0430\u043D\u043E\u0432\u043E \u2014 \u044D\u0442\u043E \u0443\u0436\u0435 \u0441\u0434\u0435\u043B\u0430\u043B CI \u043D\u0430 \u044D\u0442\u043E\u043C \u0436\u0435 \u043A\u043E\u043C\u043C\u0438\u0442\u0435 (\u0434\u0432\u043E\u0439\u043D\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430 \u043E\u0442\u043C\u0435\u043D\u0451\u043D, TASWM-249). \u041F\u0440\u043E\u0447\u0438\u0442\u0430\u0439 \u0435\u0433\u043E \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 \u0441\u0430\u043C: ${readChecks}. \u0414\u0430\u043B\u044C\u0448\u0435 \u0442\u0440\u0438 \u0440\u0430\u0437\u043D\u044B\u0445 \u0441\u043B\u0443\u0447\u0430\u044F: \u2460 \u0445\u043E\u0442\u044C \u043E\u0434\u0438\u043D \u0447\u0435\u043A \u0435\u0449\u0451 PENDING \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u043E\u043E\u0431\u0449\u0435 \u043D\u0435 \u0432\u044B\u043D\u043E\u0441\u0438: \u043D\u0435 \u043F\u043E\u0441\u0442\u0438 REQUEST CHANGES, \u043D\u0435 \u0437\u043E\u0432\u0438 done(), \u043F\u0440\u043E\u0441\u0442\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0441\u0435\u0441\u0441\u0438\u044E \u043A\u0430\u043A \u0435\u0441\u0442\u044C \u2014 \u0440\u043E\u0439 \u0441\u0430\u043C \u0437\u0430\u043C\u0435\u0442\u0438\u0442, \u0447\u0442\u043E \u0442\u044B \u0436\u0434\u0451\u0448\u044C CI, \u0438 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442 \u0434\u043E\u0434\u0435\u043B\u0430\u0442\u044C \u0440\u0435\u0432\u044C\u044E, \u043A\u043E\u0433\u0434\u0430 \u0432\u0441\u0435 \u0447\u0435\u043A\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0442\u0441\u044F (\u044D\u0442\u043E \u0448\u0442\u0430\u0442\u043D\u044B\u0439 \u043F\u0443\u0442\u044C, \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u0437\u0432\u0430\u0442\u044C \u043D\u0435 \u043D\u0443\u0436\u043D\u043E). \u2461 \u0432\u0441\u0435 \u0447\u0435\u043A\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0438\u0441\u044C \u0438 \u0435\u0441\u0442\u044C FAIL \u2014 REQUEST CHANGES, \u043D\u0430\u0437\u043E\u0432\u0438 \u043F\u043E\u0438\u043C\u0451\u043D\u043D\u043E, \u0447\u0442\u043E \u0443\u043F\u0430\u043B\u043E. \u2462 \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043C\u0438\u0442\u0435 \u0447\u0435\u043A\u043E\u0432 \u043D\u0435\u0442 \u0432\u043E\u0432\u0441\u0435 \u2014 \u0442\u043E\u0436\u0435 REQUEST CHANGES: \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u043C (\u0443 \u044D\u0442\u043E\u0433\u043E \u043F\u0440\u043E\u0435\u043A\u0442\u0430 verifyMode=ci, \u0442\u043E \u0435\u0441\u0442\u044C CI \u043E\u0431\u044F\u0437\u0430\u043D \u0431\u044B\u0442\u044C). ${dontTrustAuthor}`;
|
|
19424
19575
|
}
|
|
19425
|
-
var REVIEW_SKILL_PATH =
|
|
19576
|
+
var REVIEW_SKILL_PATH = join35(bundledRoleDir("drone"), "skills", "taskless-review", "SKILL.md");
|
|
19426
19577
|
function reviewDefectClasses(skillPath = REVIEW_SKILL_PATH) {
|
|
19427
19578
|
const text = readFileSync25(skillPath, "utf8");
|
|
19428
19579
|
const m = /^## Классы дефектов\n([\s\S]*?)(?=^## |(?![\s\S]))/m.exec(text);
|
|
@@ -19663,7 +19814,7 @@ async function resurrectStormDead(deps, worktreeExists, hasTranscript, opts) {
|
|
|
19663
19814
|
if (countActiveDrones(deps.db) >= cap) break;
|
|
19664
19815
|
if (!await projectQuotaGateAllows(deps, run)) continue;
|
|
19665
19816
|
const wt = run.worktree_path;
|
|
19666
|
-
const mode = !!wt && worktreeExists(wt) && hasTranscript(
|
|
19817
|
+
const mode = !!wt && worktreeExists(wt) && hasTranscript(run) ? "continue" : "fresh";
|
|
19667
19818
|
deps.db.addEvent(run.id, "resumed", mode === "continue" ? STORM_RESUME_CONTINUE_TEXT : STORM_RESUME_FRESH_TEXT);
|
|
19668
19819
|
try {
|
|
19669
19820
|
await deps.respawn(run, mode);
|
|
@@ -19691,7 +19842,7 @@ async function wakeRun(deps, runId, opts = {}) {
|
|
|
19691
19842
|
const worktreeExists = deps.worktreeExists ?? existsSync20;
|
|
19692
19843
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
19693
19844
|
const wt = run.worktree_path;
|
|
19694
|
-
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(
|
|
19845
|
+
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(run);
|
|
19695
19846
|
const isService = run.reviews_run_id != null || run.resolves_run_id != null;
|
|
19696
19847
|
if (!canContinue && isService) {
|
|
19697
19848
|
return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u2014 \u0441\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0439 (\u0440\u0435\u0432\u044C\u044E\u0435\u0440/\u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440), \u0430 \u0441\u0435\u0441\u0441\u0438\u044E \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u043C: fresh \u043F\u043E\u0442\u0435\u0440\u044F\u043B \u0431\u044B \u0435\u0433\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442` };
|
|
@@ -19813,7 +19964,7 @@ function repoHasCi(deps, run) {
|
|
|
19813
19964
|
function repoHasCiWorkflows(worktreePath, repo) {
|
|
19814
19965
|
for (const base3 of [worktreePath, repo]) {
|
|
19815
19966
|
if (!base3) continue;
|
|
19816
|
-
const dir =
|
|
19967
|
+
const dir = join35(base3, ".github", "workflows");
|
|
19817
19968
|
try {
|
|
19818
19969
|
if (readdirSync14(dir).some((f) => f.endsWith(".yml") || f.endsWith(".yaml"))) return true;
|
|
19819
19970
|
} catch {
|
|
@@ -19904,7 +20055,7 @@ async function wakeAuthorOnRedCi(deps, run, now, ci, worktreeExists, hasTranscri
|
|
|
19904
20055
|
}
|
|
19905
20056
|
if (stats.lastMs != null && now - stats.lastMs < CI_WAKE_COOLDOWN_MS) return false;
|
|
19906
20057
|
const wt = run.worktree_path;
|
|
19907
|
-
if (!wt || !worktreeExists(wt) || !hasTranscript(
|
|
20058
|
+
if (!wt || !worktreeExists(wt) || !hasTranscript(run)) return false;
|
|
19908
20059
|
const summary = await deps.ci.failureSummary(ci.failing, ci.failingRunIds).catch(() => null) ?? `\u274C CI \u0443\u043F\u0430\u043B: ${ci.failing.join(", ") || "(\u0441\u043C. gh pr checks)"}`;
|
|
19909
20060
|
if (isInfraCiFailure(summary)) {
|
|
19910
20061
|
const seen = ciStreak(deps.db, run.id, CI_INFRA_MARK).count;
|
|
@@ -19970,7 +20121,7 @@ async function wakeReviewerOnCiReady(deps, run, over) {
|
|
|
19970
20121
|
}
|
|
19971
20122
|
const wt = reviewer.worktree_path;
|
|
19972
20123
|
const stats = ciWakeStats(deps.db, reviewer.id);
|
|
19973
|
-
const canContinue = !!wt && worktreeAndTranscriptAlive(deps,
|
|
20124
|
+
const canContinue = !!wt && worktreeAndTranscriptAlive(deps, reviewer);
|
|
19974
20125
|
if (stats.count >= CI_WAKE_LIMIT || !canContinue) {
|
|
19975
20126
|
const why = !canContinue ? "worktree/\u0441\u0435\u0441\u0441\u0438\u044F \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B \u2014 \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E" : `${CI_WAKE_LIMIT} \u043F\u043E\u0431\u0443\u0434(\u043E\u043A) \u043F\u043E \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 CI \u043D\u0435 \u0445\u0432\u0430\u0442\u0438\u043B\u043E`;
|
|
19976
20127
|
deps.db.updateRun(reviewer.id, { status: "stalled" });
|
|
@@ -20013,10 +20164,11 @@ async function wakeReviewerOnCiReady(deps, run, over) {
|
|
|
20013
20164
|
}
|
|
20014
20165
|
return true;
|
|
20015
20166
|
}
|
|
20016
|
-
function worktreeAndTranscriptAlive(deps,
|
|
20167
|
+
function worktreeAndTranscriptAlive(deps, run) {
|
|
20017
20168
|
const exists = deps.worktreeExists ?? existsSync20;
|
|
20018
20169
|
const transcript = deps.hasTranscript ?? executorHasSession;
|
|
20019
|
-
|
|
20170
|
+
const wt = run.worktree_path;
|
|
20171
|
+
return !!wt && exists(wt) && transcript(run);
|
|
20020
20172
|
}
|
|
20021
20173
|
async function projectWantsChecks(deps, run) {
|
|
20022
20174
|
if (!deps.autoReviewForRepo) return true;
|
|
@@ -20196,7 +20348,7 @@ async function zoneConflictFor(db, models, activeFiles, run, repo, repoFallback)
|
|
|
20196
20348
|
activeFiles.prune(holders.map((r) => r.id));
|
|
20197
20349
|
const fileExists = (p) => {
|
|
20198
20350
|
try {
|
|
20199
|
-
return statSync13(
|
|
20351
|
+
return statSync13(join35(repo, p), { throwIfNoEntry: false })?.isFile() ?? false;
|
|
20200
20352
|
} catch {
|
|
20201
20353
|
return false;
|
|
20202
20354
|
}
|
|
@@ -20610,7 +20762,7 @@ async function resumeWatchdogParked(deps, droneCap) {
|
|
|
20610
20762
|
const worktreeExists = deps.worktreeExists ?? existsSync20;
|
|
20611
20763
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
20612
20764
|
const wt = candidate.worktree_path;
|
|
20613
|
-
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(
|
|
20765
|
+
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(candidate);
|
|
20614
20766
|
const mode = canContinue ? "continue" : "fresh";
|
|
20615
20767
|
if (mode === "continue" && wt && deps.reclaimWorktree) {
|
|
20616
20768
|
try {
|
|
@@ -20831,7 +20983,7 @@ async function manageRelief(deps, now, droneCap, stormActive) {
|
|
|
20831
20983
|
console.error(`[overmind] \u0441\u043D\u0430\u043F\u0448\u043E\u0442 worktree \u043F\u0430\u0443\u0437\u043D\u0443\u0442\u043E\u0433\u043E ${droneLabel(candidate)} \u0441\u043E\u0440\u0432\u0430\u043B\u0441\u044F:`, e);
|
|
20832
20984
|
}
|
|
20833
20985
|
}
|
|
20834
|
-
const canContinue = !!candidate.worktree_path && worktreeAndTranscriptAlive(deps, candidate
|
|
20986
|
+
const canContinue = !!candidate.worktree_path && worktreeAndTranscriptAlive(deps, candidate);
|
|
20835
20987
|
deps.db.updateRun(candidate.id, { status: "paused", stop_reason: RELIEF_PAUSE_REASON });
|
|
20836
20988
|
deps.db.addEvent(candidate.id, "paused", reliefPauseReason(canContinue, decisionText));
|
|
20837
20989
|
state.lastPauseAt = now;
|
|
@@ -20910,7 +21062,7 @@ async function manageRelief(deps, now, droneCap, stormActive) {
|
|
|
20910
21062
|
if (now - loopEntry.lastPauseAt < reliefBackoffMs(loopEntry.pauses, cfg.rampWindowMs, loopCfg)) return;
|
|
20911
21063
|
}
|
|
20912
21064
|
const wt = resumeCandidate.worktree_path;
|
|
20913
|
-
const canContinue = !!wt && worktreeAndTranscriptAlive(deps,
|
|
21065
|
+
const canContinue = !!wt && worktreeAndTranscriptAlive(deps, resumeCandidate);
|
|
20914
21066
|
const mode = canContinue ? "continue" : "fresh";
|
|
20915
21067
|
if (mode === "continue" && wt && deps.reclaimWorktree) {
|
|
20916
21068
|
try {
|
|
@@ -21895,7 +22047,7 @@ async function tickOnce(deps) {
|
|
|
21895
22047
|
const abort = aborted ? deps.exitAbort?.(run) ?? null : null;
|
|
21896
22048
|
const what = aborted ? abortReason(abort) : EXITED_NO_DONE_REASON;
|
|
21897
22049
|
const wt = run.worktree_path;
|
|
21898
|
-
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(
|
|
22050
|
+
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(run);
|
|
21899
22051
|
if (deps.respawn && canContinue && run.respawns < RESPAWN_LIMIT) {
|
|
21900
22052
|
const n = deps.db.incRespawn(run.id);
|
|
21901
22053
|
deps.db.addEvent(
|
|
@@ -21932,7 +22084,7 @@ async function tickOnce(deps) {
|
|
|
21932
22084
|
deps.respawn && run.respawns < RESPAWN_LIMIT) {
|
|
21933
22085
|
const wt = run.worktree_path;
|
|
21934
22086
|
const wtAlive = !!wt && worktreeExists(wt);
|
|
21935
|
-
const canContinue = wtAlive && hasTranscript(
|
|
22087
|
+
const canContinue = wtAlive && hasTranscript(run);
|
|
21936
22088
|
const young = now - lastActivityMs(deps.db, run) < RESPAWN_YOUNG_MS;
|
|
21937
22089
|
const mode = young ? "fresh" : canContinue ? "continue" : null;
|
|
21938
22090
|
if (mode) {
|
|
@@ -21989,7 +22141,7 @@ async function tickOnce(deps) {
|
|
|
21989
22141
|
}
|
|
21990
22142
|
if (!alive && outcome === "graceful" && run.status === "running" && isSubrun(run)) {
|
|
21991
22143
|
const wt = run.worktree_path;
|
|
21992
|
-
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(
|
|
22144
|
+
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(run);
|
|
21993
22145
|
if (deps.respawn && canContinue && run.respawns < RESPAWN_LIMIT) {
|
|
21994
22146
|
const n = deps.db.incRespawn(run.id);
|
|
21995
22147
|
deps.db.addEvent(run.id, "respawned", `\u0430\u0432\u0442\u043E-\u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u0435 #${n}: ${SUBRUN_EXITED_TEXT} \u2014 --continue \u0441 \u043D\u0430\u043A\u0430\u0437\u043E\u043C \u043D\u0435 \u0436\u0434\u0430\u0442\u044C \u0444\u043E\u043D\u043E\u0432\u0443\u044E \u0440\u0430\u0431\u043E\u0442\u0443 \u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C \u0441\u043E\u0440\u0430\u0437\u043C\u0435\u0440\u043D\u043E \u043F\u0440\u0430\u0432\u043A\u0435 (TASWM-528)`);
|
|
@@ -22065,7 +22217,7 @@ async function tickOnce(deps) {
|
|
|
22065
22217
|
if (run.pid != null && deps.isAlive(run.pid)) continue;
|
|
22066
22218
|
if (!await projectQuotaGateAllows(deps, run)) continue;
|
|
22067
22219
|
const wt = run.worktree_path;
|
|
22068
|
-
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(
|
|
22220
|
+
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(run);
|
|
22069
22221
|
const mode = canContinue ? "continue" : "fresh";
|
|
22070
22222
|
deps.db.addEvent(
|
|
22071
22223
|
run.id,
|
|
@@ -22295,7 +22447,7 @@ async function resolveConflict(deps, reviewRun) {
|
|
|
22295
22447
|
model: reviewRun.model,
|
|
22296
22448
|
review_model: reviewRun.review_model
|
|
22297
22449
|
});
|
|
22298
|
-
const wt =
|
|
22450
|
+
const wt = join35(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-resolve-${resolver.id}`);
|
|
22299
22451
|
try {
|
|
22300
22452
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
22301
22453
|
await deps.git.setWorktreeIdentity(wt, resolver.name);
|
|
@@ -22375,7 +22527,7 @@ async function spawnReviewer(deps, reviewRun, files) {
|
|
|
22375
22527
|
// (своей у ревьюера нет, TASWM-204), и коммент с вердиктом должен уехать именно туда.
|
|
22376
22528
|
workspace_id: reviewRun.workspace_id
|
|
22377
22529
|
});
|
|
22378
|
-
const wt =
|
|
22530
|
+
const wt = join35(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-review-${reviewer.id}`);
|
|
22379
22531
|
try {
|
|
22380
22532
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
22381
22533
|
await deps.git.setWorktreeIdentity(wt, reviewer.name);
|
|
@@ -22456,7 +22608,7 @@ async function spawnVerifier(deps, verifiedRun, files) {
|
|
|
22456
22608
|
// штамп цикла: фикс-итерацию проверяет свежий верификатор
|
|
22457
22609
|
workspace_id: verifiedRun.workspace_id
|
|
22458
22610
|
});
|
|
22459
|
-
const wt =
|
|
22611
|
+
const wt = join35(deps.worktreesDir, basename6(repo), `${verifiedRun.task_slug}-verify-${verifier.id}`);
|
|
22460
22612
|
try {
|
|
22461
22613
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
22462
22614
|
await deps.git.setWorktreeIdentity(wt, verifier.name);
|
|
@@ -22792,6 +22944,7 @@ async function runDaemon(opts) {
|
|
|
22792
22944
|
} : void 0;
|
|
22793
22945
|
let lastDroneCap = null;
|
|
22794
22946
|
let lastSwarmModels = null;
|
|
22947
|
+
const runModelRefPin = /* @__PURE__ */ new Map();
|
|
22795
22948
|
let lastSwarmEfforts = null;
|
|
22796
22949
|
let lastMaxCostUsd = null;
|
|
22797
22950
|
let lastOwnCapacity = null;
|
|
@@ -22804,8 +22957,8 @@ async function runDaemon(opts) {
|
|
|
22804
22957
|
runLogsDir: RUN_LOGS_DIR,
|
|
22805
22958
|
// TASWM-419: чем именуется каталог лога прогона — стабильные ключи из стора, не номер.
|
|
22806
22959
|
runLogKeys: (run) => db.runLogKeys(run.id),
|
|
22807
|
-
//
|
|
22808
|
-
executor
|
|
22960
|
+
// TASWM-592: исполнитель здесь НЕ пинится — лаунчер берёт адаптер ПО ПРОГОНУ (реестр
|
|
22961
|
+
// провайдеров по ссылке модели). Ручной пин cfg.executor остаётся у тестов.
|
|
22809
22962
|
droneIsolation: DRONE_ISOLATION,
|
|
22810
22963
|
channel: droneChannelLaunch,
|
|
22811
22964
|
droneCap: () => lastDroneCap,
|
|
@@ -22817,6 +22970,10 @@ async function runDaemon(opts) {
|
|
|
22817
22970
|
recordEffort: (runId, effort) => {
|
|
22818
22971
|
db.updateRun(runId, { effort });
|
|
22819
22972
|
},
|
|
22973
|
+
recordModelRef: (runId, ref) => {
|
|
22974
|
+
if (runModelRefPin.size > 4096) runModelRefPin.clear();
|
|
22975
|
+
runModelRefPin.set(runId, ref);
|
|
22976
|
+
},
|
|
22820
22977
|
maxCostUsd: () => lastMaxCostUsd,
|
|
22821
22978
|
// TASWM-535: поднятый потолок тяжёлого прогона живёт в его ленте — спавн читает её
|
|
22822
22979
|
// оттуда же, откуда потом прочитает прод-сторож.
|
|
@@ -22891,7 +23048,9 @@ async function runDaemon(opts) {
|
|
|
22891
23048
|
// реконсайла и его возраст. Возраст, а не момент: часы машины и прода
|
|
22892
23049
|
// расходятся (ноутбук после сна — на минуты), а сверяет признак прод со своим
|
|
22893
23050
|
// настенным временем. Не «демон запущен», а «цикл прокрутился».
|
|
22894
|
-
tickPulse(tickHealth, Date.now(), tickIntervalMs)
|
|
23051
|
+
tickPulse(tickHealth, Date.now(), tickIntervalMs),
|
|
23052
|
+
// TASWM-592: установленные агенты машины — справочник для парка машин.
|
|
23053
|
+
cachedInstalledExecutorIds()
|
|
22895
23054
|
);
|
|
22896
23055
|
if (reply.maxDrones !== void 0) operatorMaxDrones = reply.maxDrones;
|
|
22897
23056
|
if (reply.commands.length) onHostCommands(reply.commands);
|
|
@@ -23330,16 +23489,18 @@ async function runDaemon(opts) {
|
|
|
23330
23489
|
isAlive: pidAlive,
|
|
23331
23490
|
// Учёт cost/usage (TASWM-188): реальный аккаунтант поверх RUN_LOGS_DIR + db.
|
|
23332
23491
|
// TASWM-364: swarmModels — ТОТ ЖЕ снимок, что уехал в launcher: цена токенов считается
|
|
23333
|
-
// по модели, на которой прогон реально работал.
|
|
23334
|
-
|
|
23492
|
+
// по модели, на которой прогон реально работал. TASWM-592: исполнитель НЕ пинится —
|
|
23493
|
+
// аккаунтант сам резолвит его по прогону (итог писал харнесс ЭТОГО прогона).
|
|
23494
|
+
accountCost: makeCostAccountant({ runLogsDir: RUN_LOGS_DIR, db, swarmModels: () => lastSwarmModels, modelRefFor: (run) => runModelRefPin.get(run.id) ?? null }),
|
|
23335
23495
|
// TASWM-413: тот же аккаунтант, но поводом служит завершённый ЗАПУСК, а не смерть
|
|
23336
23496
|
// прогона в running — иначе сданный через done() прогон не получает цену никогда.
|
|
23337
|
-
sweepCost: makeCostSweeper({ runLogsDir: RUN_LOGS_DIR, db,
|
|
23497
|
+
sweepCost: makeCostSweeper({ runLogsDir: RUN_LOGS_DIR, db, swarmModels: () => lastSwarmModels, isAlive: pidAlive, modelRefFor: (run) => runModelRefPin.get(run.id) ?? null }),
|
|
23338
23498
|
// Классификация выхода прогона (TASWM-229): result-файл того же RUN_LOGS_DIR — «вышел сам»
|
|
23339
23499
|
// (graceful) vs «убит на полпути» (crash). Тот же каталог, что читает accountCost.
|
|
23340
|
-
|
|
23500
|
+
// TASWM-592: исполнитель — по прогону (та же ссылка модели + живой снимок настроек).
|
|
23501
|
+
exitOutcome: makeExitClassifier(RUN_LOGS_DIR, (runId) => db.runLogKeys(runId), (run) => executorForRun(run, lastSwarmModels, process.env, runModelRefPin.get(run.id) ?? null)),
|
|
23341
23502
|
// TASWM-487: вид обрыва — из того же result-файла и теми же ключами каталога.
|
|
23342
|
-
exitAbort: makeAbortReader(RUN_LOGS_DIR, (runId) => db.runLogKeys(runId),
|
|
23503
|
+
exitAbort: makeAbortReader(RUN_LOGS_DIR, (runId) => db.runLogKeys(runId), (run) => executorForRun(run, lastSwarmModels, process.env, runModelRefPin.get(run.id) ?? null)),
|
|
23343
23504
|
// TASWM-487: жива ли работа на origin. Тот же фолбэк repo, что у removeWorktree выше:
|
|
23344
23505
|
// у прогона в основном репозитории демона run.repo пуст.
|
|
23345
23506
|
branchOnOrigin: (repo, branch) => remoteBranchExists(repo || opts.repo, branch),
|
|
@@ -23414,7 +23575,8 @@ async function runDaemon(opts) {
|
|
|
23414
23575
|
lastCommitMs: (p) => worktreeLastCommitMs(p),
|
|
23415
23576
|
// mtime транскрипта claude-сессии как liveness (TASWM-136): точнее коммитов — обновляется
|
|
23416
23577
|
// на каждое действие дрона (Edit/Read/tool-use), не только на коммит.
|
|
23417
|
-
|
|
23578
|
+
// TASWM-592: где транскрипт лежит, знает харнесс ПРОГОНА — резолвим исполнителя по модели.
|
|
23579
|
+
transcriptMtimeMs: (run) => executorForRun(run, lastSwarmModels, process.env, runModelRefPin.get(run.id) ?? null).session.mtimeMs(run.worktree_path ?? ""),
|
|
23418
23580
|
// ВЕС ЗАДАЧИ (TASWM-535): чем демон проверяет, что заявивший о тяжести дрон РЕАЛЬНО
|
|
23419
23581
|
// продвигается (коммиты от базы прогона), и от какого потолка считать ему добавку —
|
|
23420
23582
|
// от той же живой настройки роя, что уезжает харнессу на спавне.
|
|
@@ -23533,7 +23695,8 @@ async function runDaemon(opts) {
|
|
|
23533
23695
|
// TASWM-514: с этого момента считаем попытки спавна — счётчик живёт ровно одну жизнь
|
|
23534
23696
|
// процесса демона и рестарт не переживает (см. preStartAttempts).
|
|
23535
23697
|
startedAt: Date.now(),
|
|
23536
|
-
|
|
23698
|
+
// TASWM-592: run-aware — где лежит сессия, знает харнесс ПРОГОНА.
|
|
23699
|
+
hasTranscript: (run) => !!run.worktree_path && executorForRun(run, lastSwarmModels, process.env, runModelRefPin.get(run.id) ?? null).session.exists(run.worktree_path),
|
|
23537
23700
|
// CI-осведомлённость review-PR (TASWM-102): реальный `gh pr checks`/`gh run view --log-failed`
|
|
23538
23701
|
// (gh.ts) — решение (будить ли дрона) и гейты живут в tickOnce/handleCiRed. failureSummary
|
|
23539
23702
|
// строится над failing/failingRunIds ИЗ checks — без повторного `gh pr checks`.
|
|
@@ -23544,7 +23707,22 @@ async function runDaemon(opts) {
|
|
|
23544
23707
|
// Штормовой режим (TASWM-23): health-probe на отбой (реальный claude) + внешний
|
|
23545
23708
|
// сигнал SWARM_STORM_URL (2-й слой, если задан). Логика/пороги — в manageStorm/storm.ts.
|
|
23546
23709
|
storm: {
|
|
23547
|
-
|
|
23710
|
+
// TASWM-592: проба бьёт в исполнителя, на котором работают дроны (реестр провайдеров
|
|
23711
|
+
// по ссылке модели роя), — иначе рой на другом харнессе штормовал бы по вине claude
|
|
23712
|
+
// и наоборот.
|
|
23713
|
+
probe: () => {
|
|
23714
|
+
try {
|
|
23715
|
+
const ref = resolveRunModelRef({ kind: "drone" }, lastSwarmModels);
|
|
23716
|
+
return executorHealthy({
|
|
23717
|
+
executor: executorForRun({ kind: "drone" }, lastSwarmModels),
|
|
23718
|
+
timeoutMs: resolveStormConfig().probeTimeoutMs,
|
|
23719
|
+
env: providerEnv(ref.providerId, ref.model)
|
|
23720
|
+
});
|
|
23721
|
+
} catch (e) {
|
|
23722
|
+
console.error(`[storm] \u043F\u0440\u043E\u0431\u0430: \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0432\u044B\u0431\u0440\u0430\u0442\u044C \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F/env \u043F\u043E \u043C\u043E\u0434\u0435\u043B\u0438 \u0440\u043E\u044F \u2014 ${String(e)}`);
|
|
23723
|
+
return Promise.resolve({ ok: false, reason: "exec", resetHint: null });
|
|
23724
|
+
}
|
|
23725
|
+
},
|
|
23548
23726
|
// externalDown опционален (exactOptionalPropertyTypes): ключ есть только при
|
|
23549
23727
|
// заданном SWARM_STORM_URL, иначе опускаем — не передаём undefined.
|
|
23550
23728
|
...SWARM_STORM_URL ? { externalDown: () => fetchExternalStorm(SWARM_STORM_URL) } : {}
|
|
@@ -23711,7 +23889,7 @@ async function runDaemon(opts) {
|
|
|
23711
23889
|
wakeRun: async (slug) => {
|
|
23712
23890
|
const run = db.activeRunForSlug(slug);
|
|
23713
23891
|
if (!run) return null;
|
|
23714
|
-
return wakeRun({ db, isAlive: pidAlive, respawn: deps.respawn, narrate: narrator.narrate }, run.id);
|
|
23892
|
+
return wakeRun({ db, isAlive: pidAlive, respawn: deps.respawn, narrate: narrator.narrate, ...deps.hasTranscript ? { hasTranscript: deps.hasTranscript } : {} }, run.id);
|
|
23715
23893
|
},
|
|
23716
23894
|
update: async (onStep) => {
|
|
23717
23895
|
const pm = detectGlobalPm(process.argv[1]);
|
|
@@ -23861,7 +24039,7 @@ async function runDaemon(opts) {
|
|
|
23861
24039
|
// Побудка прогона по id (TASWM-240): вход для `swarm wake <id>` — тот же respawn с
|
|
23862
24040
|
// полными deps демона (getTask/verifyMode/реестр репо), что и авто-оживление, и по
|
|
23863
24041
|
// зеркалу демона, а не по короткоживущему стору CLI.
|
|
23864
|
-
wake: (runId) => wakeRun({ db, isAlive: pidAlive, respawn: deps.respawn, narrate: narrator.narrate }, runId),
|
|
24042
|
+
wake: (runId) => wakeRun({ db, isAlive: pidAlive, respawn: deps.respawn, narrate: narrator.narrate, ...deps.hasTranscript ? { hasTranscript: deps.hasTranscript } : {} }, runId),
|
|
23865
24043
|
replay: (qid, chosen) => replayDecision(
|
|
23866
24044
|
{
|
|
23867
24045
|
db,
|