@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/README.md
CHANGED
|
@@ -188,6 +188,13 @@ SWARM_EXECUTOR=codex swarm start # Codex CLI (`codex exec`)
|
|
|
188
188
|
учёт стоимости, watchdog и воскрешение после обрыва работают одинаково. Что
|
|
189
189
|
различается — прячет адаптер:
|
|
190
190
|
|
|
191
|
+
С TASWM-592 исполнитель — свойство ПРОГОНА: ссылка его модели (пин задачи →
|
|
192
|
+
настройка роя «Модель дронов» → env) выбирает харнесс через реестр провайдеров
|
|
193
|
+
(`executorForRun`). `SWARM_EXECUTOR` остался машинной ступенью: он пиннит прогоны,
|
|
194
|
+
чей провайдер не назван явно — машина «вся на codex» остаётся на codex, а прогон с
|
|
195
|
+
`openai/…` всё равно уйдёт к codex, с `anthropic/…` — к claude. Сменить модель роя
|
|
196
|
+
целиком можно и настройкой: «Модель дронов» = `openai/default`.
|
|
197
|
+
|
|
191
198
|
| | `claude-code` | `codex` |
|
|
192
199
|
|---|---|---|
|
|
193
200
|
| запуск | `claude -p <промпт>` | `codex exec <промпт>` |
|
|
@@ -197,6 +204,17 @@ SWARM_EXECUTOR=codex swarm start # Codex CLI (`codex exec`)
|
|
|
197
204
|
| сессии (живость дрона) | `~/.claude/projects/<worktree>` | `~/.taskless-swarm/executor-state/codex/<worktree>` |
|
|
198
205
|
| скиллы роли | `--plugin-dir` | текстом в промпте (флага нет) |
|
|
199
206
|
|
|
207
|
+
### GLM (Z.ai) — провайдер `zai` (интерим)
|
|
208
|
+
|
|
209
|
+
Подписка GLM Coding Plan работает через официальный Anthropic-совместимый эндпоинт
|
|
210
|
+
Z.ai, поэтому харнесс остаётся claude-code (аккаунт Anthropic не нужен). Ключ —
|
|
211
|
+
в окружении ДЕМОНА: `SWARM_ZAI_TOKEN` (консоль z.ai, CLI-ключ подписки), опционально
|
|
212
|
+
`SWARM_ZAI_BASE_URL` (прокси/регион). Переключение: «Модель дронов» = `zai/glm-5.3`
|
|
213
|
+
или поле модели задачи. Дрону ключ не наследуется — доходит только переведённый
|
|
214
|
+
`ANTHROPIC_AUTH_TOKEN`. Стоимость прогонов на подписке не измеряется (честный ноль
|
|
215
|
+
с пометкой, тарифы не выдумываем). Порядок обновления: сначала `swarm update` на
|
|
216
|
+
машинах, потом пин `zai/…` — старый демон не знает env-провязку и умрёт на спавне.
|
|
217
|
+
|
|
200
218
|
Путь к исполняемому файлу, если он не в `PATH`: `SWARM_CLAUDE_BIN` /
|
|
201
219
|
`SWARM_CODEX_BIN`. Неизвестное значение `SWARM_EXECUTOR` — ошибка на старте, а не
|
|
202
220
|
тихий откат на дефолт: «думал, поехали на другом» узнаётся иначе только из счёта.
|
package/dist/cli.js
CHANGED
|
@@ -668,6 +668,11 @@ function defaultModelFor(role, providerId = null) {
|
|
|
668
668
|
const provider = findProvider(providerId) ?? findProvider(DEFAULT_PROVIDER_ID);
|
|
669
669
|
return provider.roles[role];
|
|
670
670
|
}
|
|
671
|
+
function executorIdForModelRef(ref) {
|
|
672
|
+
const providerId = parseModelRef(ref)?.providerId ?? DEFAULT_PROVIDER_ID;
|
|
673
|
+
const provider = findProvider(providerId) ?? findProvider(DEFAULT_PROVIDER_ID);
|
|
674
|
+
return provider.executorId;
|
|
675
|
+
}
|
|
671
676
|
var HARNESS_DEFAULT_MODEL, MODEL_PROVIDERS, DEFAULT_PROVIDER_ID;
|
|
672
677
|
var init_modelPolicy = __esm({
|
|
673
678
|
"../swarm-core/src/modelPolicy.ts"() {
|
|
@@ -681,6 +686,21 @@ var init_modelPolicy = __esm({
|
|
|
681
686
|
selectable: true,
|
|
682
687
|
roles: { executor: "claude-opus-5-5", review: "claude-sonnet-5" }
|
|
683
688
|
},
|
|
689
|
+
// TASWM-593 · GLM (Z.ai), INTERIM: подписка GLM Coding Plan работает через официальный
|
|
690
|
+
// Anthropic-совместимый эндпоинт Z.ai (api.z.ai/api/anthropic), поэтому харнесс — тот же
|
|
691
|
+
// claude-code, а всё различие провайдера — env процесса дрона (BASE_URL + токен, см.
|
|
692
|
+
// providerEnv в адаптере; клаудовский аккаунт НЕ нужен — бинарь бьёт в Z.ai). Interim
|
|
693
|
+
// для роя владельца до появления `--model` у ZCode CLI: тогда провайдер переезжает на
|
|
694
|
+
// собственный zcode-адаптер (волт reference_zcode_cli_headless_research).
|
|
695
|
+
// Дефолты ролей: glm-5.3 — флагман; каталог подписки снаружи не виден, имя сверено
|
|
696
|
+
// живой пробой, выдуманных имён в реестре нет — настройкой можно поставить любую.
|
|
697
|
+
{
|
|
698
|
+
id: "zai",
|
|
699
|
+
label: "GLM (Z.ai)",
|
|
700
|
+
executorId: "claude-code",
|
|
701
|
+
selectable: true,
|
|
702
|
+
roles: { executor: "glm-5.3", review: "glm-5.3" }
|
|
703
|
+
},
|
|
684
704
|
// TASWM-565 · шаг 1 из четырёх: провайдер в реестре ЕСТЬ, выбрать его ещё нельзя.
|
|
685
705
|
// Имён моделей здесь нет СОЗНАТЕЛЬНО: у владельца подписка, состав моделей в ней нам
|
|
686
706
|
// неизвестен, а выдуманное имя хуже маркера «решает Codex».
|
|
@@ -2294,10 +2314,25 @@ function writeMcpConfig(spec) {
|
|
|
2294
2314
|
writeFileSync2(path, JSON.stringify(cfg, null, 2));
|
|
2295
2315
|
return path;
|
|
2296
2316
|
}
|
|
2317
|
+
function providerEnv(providerId, model, env = process.env) {
|
|
2318
|
+
const compat = ANTHROPIC_COMPAT_PROVIDERS[providerId];
|
|
2319
|
+
if (!compat) return {};
|
|
2320
|
+
const token = (env[compat.tokenEnv] ?? "").trim();
|
|
2321
|
+
if (!token) {
|
|
2322
|
+
throw new Error(
|
|
2323
|
+
`\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.`
|
|
2324
|
+
);
|
|
2325
|
+
}
|
|
2326
|
+
return {
|
|
2327
|
+
ANTHROPIC_BASE_URL: (env[compat.baseUrlEnv] ?? "").trim() || compat.baseUrlDefault,
|
|
2328
|
+
ANTHROPIC_AUTH_TOKEN: token,
|
|
2329
|
+
...model ? { ANTHROPIC_MODEL: model, ANTHROPIC_SMALL_FAST_MODEL: model } : {}
|
|
2330
|
+
};
|
|
2331
|
+
}
|
|
2297
2332
|
function abortField(abort) {
|
|
2298
2333
|
return abort ? { aborted: abort } : {};
|
|
2299
2334
|
}
|
|
2300
|
-
var ISOLATION_FLAGS, MCP_CONFIG_FILE, claudeCodeExecutor;
|
|
2335
|
+
var ISOLATION_FLAGS, MCP_CONFIG_FILE, ANTHROPIC_COMPAT_PROVIDERS, PROVIDER_TOKEN_ENV, claudeCodeExecutor;
|
|
2301
2336
|
var init_claudeCode = __esm({
|
|
2302
2337
|
"src/executors/claudeCode.ts"() {
|
|
2303
2338
|
"use strict";
|
|
@@ -2308,21 +2343,31 @@ var init_claudeCode = __esm({
|
|
|
2308
2343
|
init_transcriptSpend();
|
|
2309
2344
|
ISOLATION_FLAGS = ["--setting-sources", "project", "--strict-mcp-config"];
|
|
2310
2345
|
MCP_CONFIG_FILE = SWARM_MCP_CONFIG_FILE;
|
|
2346
|
+
ANTHROPIC_COMPAT_PROVIDERS = {
|
|
2347
|
+
zai: {
|
|
2348
|
+
baseUrlDefault: "https://api.z.ai/api/anthropic",
|
|
2349
|
+
baseUrlEnv: "SWARM_ZAI_BASE_URL",
|
|
2350
|
+
tokenEnv: "SWARM_ZAI_TOKEN"
|
|
2351
|
+
}
|
|
2352
|
+
};
|
|
2353
|
+
PROVIDER_TOKEN_ENV = Object.values(ANTHROPIC_COMPAT_PROVIDERS).map((p) => p.tokenEnv);
|
|
2311
2354
|
claudeCodeExecutor = {
|
|
2312
2355
|
id: "claude-code",
|
|
2313
2356
|
title: "Claude Code (claude)",
|
|
2314
2357
|
binEnv: "SWARM_CLAUDE_BIN",
|
|
2315
2358
|
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" },
|
|
2316
|
-
resolveBin: () => resolveClaudeBin(),
|
|
2359
|
+
resolveBin: (env) => resolveClaudeBin(env ? { env } : {}),
|
|
2317
2360
|
// Стрелка, а не метод: `plan` могут вызвать в отрыве от объекта (деструктуризация в тестах,
|
|
2318
2361
|
// проброс колбэком), и `this` тогда уедет в undefined.
|
|
2319
2362
|
plan: (spec) => {
|
|
2320
2363
|
const cfgPath = writeMcpConfig(spec);
|
|
2321
2364
|
const hook = droneMergeGateHookPath(spec.worktree);
|
|
2322
2365
|
const settings = spec.denyPaths.length > 0 || hook ? droneSettingsJson(spec.denyPaths, hook) : void 0;
|
|
2366
|
+
const providerEnvVars = providerEnv(spec.providerId, spec.model);
|
|
2323
2367
|
return {
|
|
2324
2368
|
bin: resolveClaudeBin(),
|
|
2325
|
-
args: claudeArgs(cfgPath, spec.prompt, spec.continueSession, spec.model, spec.skillDirs, settings, spec.maxBudgetUsd, spec.effort)
|
|
2369
|
+
args: claudeArgs(cfgPath, spec.prompt, spec.continueSession, spec.model, spec.skillDirs, settings, spec.maxBudgetUsd, spec.effort),
|
|
2370
|
+
...Object.keys(providerEnvVars).length > 0 ? { env: providerEnvVars } : {}
|
|
2326
2371
|
};
|
|
2327
2372
|
},
|
|
2328
2373
|
parseResult(raw, model) {
|
|
@@ -2374,7 +2419,12 @@ var init_claudeCode = __esm({
|
|
|
2374
2419
|
probe: {
|
|
2375
2420
|
args: [...ISOLATION_FLAGS, "-p", "ok", "--max-turns", "1"],
|
|
2376
2421
|
// TASWM-214: исчерпанный лимит подписки — НЕ шторм провайдера, и путать их нельзя.
|
|
2377
|
-
limitPattern: /(weekly limit|usage limit|hit your .*limit)/i
|
|
2422
|
+
limitPattern: /(weekly limit|usage limit|hit your .*limit)/i,
|
|
2423
|
+
// TASWM-592: отвергнутый доступ — не «шторм»: ключ протух, аккаунт забанен/отключён,
|
|
2424
|
+
// регион закрыт (403 без VPN — живой кейс этого роя). Коды — только в контексте
|
|
2425
|
+
// ошибки (ревью PR #1482): голое «403» в arbitrary-выводе и POSIX permission denied
|
|
2426
|
+
// от локальной ФС советовали бы «чинить вход» не про ту болезнь.
|
|
2427
|
+
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
|
|
2378
2428
|
}
|
|
2379
2429
|
};
|
|
2380
2430
|
}
|
|
@@ -2505,7 +2555,7 @@ var init_codex = __esm({
|
|
|
2505
2555
|
title: "Codex CLI (codex)",
|
|
2506
2556
|
binEnv: "SWARM_CODEX_BIN",
|
|
2507
2557
|
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" },
|
|
2508
|
-
resolveBin: () => resolveCodexBin(),
|
|
2558
|
+
resolveBin: (env) => resolveCodexBin(env ?? process.env),
|
|
2509
2559
|
// Стрелка, а не метод — см. тот же комментарий в claudeCode.ts.
|
|
2510
2560
|
plan: (spec) => {
|
|
2511
2561
|
const notice = skillsNotice(spec.skillDirs);
|
|
@@ -2554,12 +2604,48 @@ ${notice}` : spec.prompt;
|
|
|
2554
2604
|
// Тот же принцип, что у claude: дешёвый реальный прогон — он проверяет и auth, и живость
|
|
2555
2605
|
// провайдера разом. Один короткий ответ, без тулов и без сети сверх модели.
|
|
2556
2606
|
args: ["exec", "--json", SKIP_GIT_CHECK_FLAG, BYPASS_FLAG, "ok"],
|
|
2607
|
+
// TASWM-592: не вошёл/ключ отвергнут — не шторм; точные формулировки codex уточним
|
|
2608
|
+
// на живом исполнителе, поэтому паттерн широкий.
|
|
2609
|
+
authPattern: /\b(401|403)\b|not logged in|unauthorized|authentication|invalid api key/i,
|
|
2557
2610
|
limitPattern: /(usage limit|rate limit|quota exceeded|insufficient_quota)/i
|
|
2558
2611
|
}
|
|
2559
2612
|
};
|
|
2560
2613
|
}
|
|
2561
2614
|
});
|
|
2562
2615
|
|
|
2616
|
+
// src/swarmModels.ts
|
|
2617
|
+
function pick2(v) {
|
|
2618
|
+
const s = typeof v === "string" ? v.trim() : "";
|
|
2619
|
+
return s.length > 0 ? s : null;
|
|
2620
|
+
}
|
|
2621
|
+
function isReviewKind(kind) {
|
|
2622
|
+
return kind === "reviewer" || kind === "verifier";
|
|
2623
|
+
}
|
|
2624
|
+
function modelRoleOf(kind) {
|
|
2625
|
+
return isReviewKind(kind) ? "review" : "executor";
|
|
2626
|
+
}
|
|
2627
|
+
function forKind(kind, swarm, norm) {
|
|
2628
|
+
if (!swarm) return null;
|
|
2629
|
+
return isReviewKind(kind) ? norm(swarm.reviewer) ?? norm(swarm.drone) : norm(swarm.drone);
|
|
2630
|
+
}
|
|
2631
|
+
function resolveRunModelRef(run, swarm, env = process.env) {
|
|
2632
|
+
const configured = parseModelRef(
|
|
2633
|
+
pick2(run.model) ?? forKind(run.kind, swarm, pick2) ?? pick2(env.SWARM_DRONE_MODEL)
|
|
2634
|
+
);
|
|
2635
|
+
if (configured) return configured;
|
|
2636
|
+
const providerId = pick2(env.SWARM_MODEL_PROVIDER) ?? DEFAULT_PROVIDER_ID;
|
|
2637
|
+
return { providerId, model: defaultModelFor(modelRoleOf(run.kind), providerId) };
|
|
2638
|
+
}
|
|
2639
|
+
function resolveRunEffort(run, swarm, env = process.env) {
|
|
2640
|
+
return toEffortLevel(run.effort) ?? forKind(run.kind, swarm, toEffortLevel) ?? toEffortLevel(env.SWARM_DRONE_EFFORT) ?? null;
|
|
2641
|
+
}
|
|
2642
|
+
var init_swarmModels = __esm({
|
|
2643
|
+
"src/swarmModels.ts"() {
|
|
2644
|
+
"use strict";
|
|
2645
|
+
init_src();
|
|
2646
|
+
}
|
|
2647
|
+
});
|
|
2648
|
+
|
|
2563
2649
|
// src/executors/types.ts
|
|
2564
2650
|
var init_types2 = __esm({
|
|
2565
2651
|
"src/executors/types.ts"() {
|
|
@@ -2577,12 +2663,22 @@ function resolveExecutor(id) {
|
|
|
2577
2663
|
}
|
|
2578
2664
|
return found;
|
|
2579
2665
|
}
|
|
2666
|
+
function executorForRun(run, swarm, env = process.env, pinned = null) {
|
|
2667
|
+
const ref = pinned ?? resolveRunModelRef(run, swarm, env);
|
|
2668
|
+
if (ref.providerId === DEFAULT_PROVIDER_ID) {
|
|
2669
|
+
const envExecutor = (env.SWARM_EXECUTOR ?? "").trim();
|
|
2670
|
+
if (envExecutor) return resolveExecutor(envExecutor);
|
|
2671
|
+
}
|
|
2672
|
+
return resolveExecutor(executorIdForModelRef(`${ref.providerId}/${ref.model}`));
|
|
2673
|
+
}
|
|
2580
2674
|
var DEFAULT_EXECUTOR_ID, EXECUTORS;
|
|
2581
2675
|
var init_executors = __esm({
|
|
2582
2676
|
"src/executors/index.ts"() {
|
|
2583
2677
|
"use strict";
|
|
2678
|
+
init_src();
|
|
2584
2679
|
init_claudeCode();
|
|
2585
2680
|
init_codex();
|
|
2681
|
+
init_swarmModels();
|
|
2586
2682
|
init_types2();
|
|
2587
2683
|
init_claudeCode();
|
|
2588
2684
|
init_codex();
|
|
@@ -3520,7 +3616,7 @@ var init_package = __esm({
|
|
|
3520
3616
|
"package.json"() {
|
|
3521
3617
|
package_default = {
|
|
3522
3618
|
name: "@taskless-app/swarm",
|
|
3523
|
-
version: "0.6.
|
|
3619
|
+
version: "0.6.59",
|
|
3524
3620
|
type: "module",
|
|
3525
3621
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
3526
3622
|
keywords: [
|
|
@@ -3571,7 +3667,7 @@ var init_package = __esm({
|
|
|
3571
3667
|
execa: "^10.0.0",
|
|
3572
3668
|
zod: "3.25.76"
|
|
3573
3669
|
},
|
|
3574
|
-
gitHead: "
|
|
3670
|
+
gitHead: "13cdd65f2929d86153c0a52715c0d9891f8a1e4b"
|
|
3575
3671
|
};
|
|
3576
3672
|
}
|
|
3577
3673
|
});
|
|
@@ -6021,6 +6117,13 @@ function blockedTroubles(f) {
|
|
|
6021
6117
|
fixes: [{ what: "\u0434\u043E\u0436\u0434\u0430\u0442\u044C\u0441\u044F \u0440\u0435\u0437\u0435\u0442\u0430 \u043B\u0438\u043C\u0438\u0442\u0430; \u0440\u0430\u0431\u043E\u0442\u0430 \u0438\u0437 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u043D\u0438\u043A\u0443\u0434\u0430 \u043D\u0435 \u0434\u0435\u043D\u0435\u0442\u0441\u044F", command: "swarm status # \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C \u043F\u043E\u0441\u043B\u0435 \u0440\u0435\u0437\u0435\u0442\u0430" }]
|
|
6022
6118
|
});
|
|
6023
6119
|
}
|
|
6120
|
+
if (work.storm?.reason === "auth") {
|
|
6121
|
+
out.push({
|
|
6122
|
+
id: "storm-auth",
|
|
6123
|
+
why: "\u0434\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 (\u043A\u043B\u044E\u0447/\u0430\u043A\u043A\u0430\u0443\u043D\u0442/\u0440\u0435\u0433\u0438\u043E\u043D) \u2014 \u043F\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: \u0440\u0435\u0442\u0440\u0430\u0438 \u0442\u0443\u0442 \u0431\u0435\u0441\u0441\u0438\u043B\u044C\u043D\u044B",
|
|
6124
|
+
fixes: [{ what: "\u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0432\u0445\u043E\u0434 \u0438 \u043A\u043B\u044E\u0447 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F", command: "swarm doctor" }]
|
|
6125
|
+
});
|
|
6126
|
+
}
|
|
6024
6127
|
if (work.storm?.reason === "provider") {
|
|
6025
6128
|
out.push({
|
|
6026
6129
|
id: "storm-provider",
|
|
@@ -6158,7 +6261,7 @@ function detailLines(f) {
|
|
|
6158
6261
|
if (otherLine.length) lines.push(` \u0435\u0449\u0451 \u0432 \u0440\u043E\u0435: ${otherLine.map(([st, n]) => `${st} ${n}`).join(", ")}`);
|
|
6159
6262
|
if (w.storm) {
|
|
6160
6263
|
lines.push(
|
|
6161
|
-
` ${w.storm.reason === "limit" ? `\u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u043B\u0438\u043C\u0438\u0442 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F${w.storm.hint ? ` (${w.storm.hint})` : ""}` : "\u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 (\u0448\u0442\u043E\u0440\u043C \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430)"}`
|
|
6264
|
+
` ${w.storm.reason === "limit" ? `\u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u043B\u0438\u043C\u0438\u0442 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F${w.storm.hint ? ` (${w.storm.hint})` : ""}` : w.storm.reason === "auth" ? "\u0434\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 (\u043A\u043B\u044E\u0447/\u0430\u043A\u043A\u0430\u0443\u043D\u0442/\u0440\u0435\u0433\u0438\u043E\u043D)" : "\u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 (\u0448\u0442\u043E\u0440\u043C \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430)"}`
|
|
6162
6265
|
);
|
|
6163
6266
|
}
|
|
6164
6267
|
return lines;
|
|
@@ -9245,7 +9348,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
9245
9348
|
*
|
|
9246
9349
|
* Прод недоступен / ответ без поручений → пустые списки (демон работает дальше).
|
|
9247
9350
|
*/
|
|
9248
|
-
async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick) {
|
|
9351
|
+
async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick, executors) {
|
|
9249
9352
|
try {
|
|
9250
9353
|
const args = this.repoFullNames.length ? { repoFullNames: this.repoFullNames } : {};
|
|
9251
9354
|
if (daemonVersion) args.daemonVersion = daemonVersion;
|
|
@@ -9275,6 +9378,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
9275
9378
|
if (tick && Number.isFinite(tick.seq) && Number.isFinite(tick.ageMs) && tick.intervalMs > 0) {
|
|
9276
9379
|
args.tick = { seq: Math.max(0, Math.trunc(tick.seq)), ageMs: Math.max(0, Math.trunc(tick.ageMs)), intervalMs: Math.trunc(tick.intervalMs) };
|
|
9277
9380
|
}
|
|
9381
|
+
if (executors) args.executorsAvailable = executors;
|
|
9278
9382
|
const res = await this.call("swarm_heartbeat", args);
|
|
9279
9383
|
if (res?.isError) return EMPTY_HEARTBEAT_REPLY;
|
|
9280
9384
|
this.heartbeatOkAt = this.now();
|
|
@@ -10825,8 +10929,9 @@ function stormFacts(db2) {
|
|
|
10825
10929
|
if (!db2.stormActive()) return null;
|
|
10826
10930
|
const since = db2.currentStormStartedAt();
|
|
10827
10931
|
const recs = since ? db2.stormProbeRecordsSince(since) : [];
|
|
10932
|
+
const last = lastProbeReason(recs);
|
|
10828
10933
|
return {
|
|
10829
|
-
reason:
|
|
10934
|
+
reason: last === "limit" ? "limit" : last === "auth" ? "auth" : "provider",
|
|
10830
10935
|
hint: recs.length ? recs[recs.length - 1].resetHint ?? null : null
|
|
10831
10936
|
};
|
|
10832
10937
|
}
|
|
@@ -11452,6 +11557,13 @@ function stormBanner(storm) {
|
|
|
11452
11557
|
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.`
|
|
11453
11558
|
};
|
|
11454
11559
|
}
|
|
11560
|
+
if (storm.reason === "auth") {
|
|
11561
|
+
return {
|
|
11562
|
+
ico: "\u{1F511}",
|
|
11563
|
+
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",
|
|
11564
|
+
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`)."
|
|
11565
|
+
};
|
|
11566
|
+
}
|
|
11455
11567
|
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";
|
|
11456
11568
|
return {
|
|
11457
11569
|
ico: "\u26A1",
|
|
@@ -12009,6 +12121,10 @@ function paintStorm(){ var b=document.getElementById('storm-banner'); if(!b) ret
|
|
|
12009
12121
|
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>'+
|
|
12010
12122
|
'<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>';
|
|
12011
12123
|
b.hidden=false; return; }
|
|
12124
|
+
if(st.reason==='auth'){
|
|
12125
|
+
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>'+
|
|
12126
|
+
'<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>';
|
|
12127
|
+
b.hidden=false; return; }
|
|
12012
12128
|
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';
|
|
12013
12129
|
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>'+
|
|
12014
12130
|
'<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>';
|
|
@@ -12534,39 +12650,10 @@ function droneTestEnv(droneCap, base3 = process.env, host = hostForkSpec()) {
|
|
|
12534
12650
|
};
|
|
12535
12651
|
}
|
|
12536
12652
|
|
|
12537
|
-
// src/swarmModels.ts
|
|
12538
|
-
init_src();
|
|
12539
|
-
function pick2(v) {
|
|
12540
|
-
const s = typeof v === "string" ? v.trim() : "";
|
|
12541
|
-
return s.length > 0 ? s : null;
|
|
12542
|
-
}
|
|
12543
|
-
function isReviewKind(kind) {
|
|
12544
|
-
return kind === "reviewer" || kind === "verifier";
|
|
12545
|
-
}
|
|
12546
|
-
function modelRoleOf(kind) {
|
|
12547
|
-
return isReviewKind(kind) ? "review" : "executor";
|
|
12548
|
-
}
|
|
12549
|
-
function forKind(kind, swarm, norm) {
|
|
12550
|
-
if (!swarm) return null;
|
|
12551
|
-
return isReviewKind(kind) ? norm(swarm.reviewer) ?? norm(swarm.drone) : norm(swarm.drone);
|
|
12552
|
-
}
|
|
12553
|
-
function resolveRunModelRef(run, swarm, env = process.env) {
|
|
12554
|
-
const configured = parseModelRef(
|
|
12555
|
-
pick2(run.model) ?? forKind(run.kind, swarm, pick2) ?? pick2(env.SWARM_DRONE_MODEL)
|
|
12556
|
-
);
|
|
12557
|
-
if (configured) return configured;
|
|
12558
|
-
const providerId = pick2(env.SWARM_MODEL_PROVIDER) ?? DEFAULT_PROVIDER_ID;
|
|
12559
|
-
return { providerId, model: defaultModelFor(modelRoleOf(run.kind), providerId) };
|
|
12560
|
-
}
|
|
12561
|
-
function resolveRunModel(run, swarm, env = process.env) {
|
|
12562
|
-
return resolveRunModelRef(run, swarm, env).model;
|
|
12563
|
-
}
|
|
12564
|
-
function resolveRunEffort(run, swarm, env = process.env) {
|
|
12565
|
-
return toEffortLevel(run.effort) ?? forKind(run.kind, swarm, toEffortLevel) ?? toEffortLevel(env.SWARM_DRONE_EFFORT) ?? null;
|
|
12566
|
-
}
|
|
12567
|
-
|
|
12568
12653
|
// src/launcher.ts
|
|
12654
|
+
init_swarmModels();
|
|
12569
12655
|
init_executors();
|
|
12656
|
+
init_claudeCode();
|
|
12570
12657
|
init_src();
|
|
12571
12658
|
var requireFromHere = createRequire(import.meta.url);
|
|
12572
12659
|
function resolveTsxCli() {
|
|
@@ -12617,7 +12704,10 @@ var DAEMON_STATE_ENV = [
|
|
|
12617
12704
|
"SWARM_OUTBOX",
|
|
12618
12705
|
"SWARM_HEARTBEAT",
|
|
12619
12706
|
"SWARM_REVIEW_CUTOFF",
|
|
12620
|
-
"TASKLESS_TOKEN"
|
|
12707
|
+
"TASKLESS_TOKEN",
|
|
12708
|
+
// TASWM-593 (ревью PR #1483): машинные ключи совместимых провайдеров — дрону не наследуются;
|
|
12709
|
+
// доходит только переведённый ANTHROPIC_AUTH_TOKEN из plan.env, который мешается ПОСЛЕ чистки.
|
|
12710
|
+
...PROVIDER_TOKEN_ENV
|
|
12621
12711
|
];
|
|
12622
12712
|
function bundledRoleDir(kind) {
|
|
12623
12713
|
return join14(dirname4(fileURLToPath2(import.meta.url)), "..", "roles", kind);
|
|
@@ -12716,13 +12806,18 @@ function makeLauncher(cfg) {
|
|
|
12716
12806
|
...run.hero_run_id != null ? { SWARM_HERO_RUN_ID: String(run.hero_run_id) } : {}
|
|
12717
12807
|
}
|
|
12718
12808
|
};
|
|
12719
|
-
const
|
|
12809
|
+
const modelRef = resolveRunModelRef(run, cfg.swarmModels?.() ?? null);
|
|
12720
12810
|
const effort = resolveRunEffort(run, cfg.swarmEfforts?.() ?? null);
|
|
12721
12811
|
try {
|
|
12722
12812
|
cfg.recordEffort?.(run.id, effort);
|
|
12723
12813
|
} catch (e) {
|
|
12724
12814
|
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)}`);
|
|
12725
12815
|
}
|
|
12816
|
+
try {
|
|
12817
|
+
cfg.recordModelRef?.(run.id, modelRef);
|
|
12818
|
+
} catch (e) {
|
|
12819
|
+
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)}`);
|
|
12820
|
+
}
|
|
12726
12821
|
const realHome = cfg.droneIsolation?.realHome ?? homedir5();
|
|
12727
12822
|
const home = cfg.droneIsolation ? buildDroneHome({ realHome, droneHome: cfg.droneIsolation.home }) : void 0;
|
|
12728
12823
|
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync9(join14(cfg.droneIsolation.swarmHome, "token"))) {
|
|
@@ -12730,12 +12825,13 @@ function makeLauncher(cfg) {
|
|
|
12730
12825
|
`[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 ${join14(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.`
|
|
12731
12826
|
);
|
|
12732
12827
|
}
|
|
12733
|
-
const executor = cfg.executor ??
|
|
12828
|
+
const executor = cfg.executor ?? executorForRun(run, cfg.swarmModels?.() ?? null);
|
|
12734
12829
|
const plan = executor.plan({
|
|
12735
12830
|
worktree,
|
|
12736
12831
|
prompt,
|
|
12737
12832
|
continueSession: !!continueSession,
|
|
12738
|
-
model,
|
|
12833
|
+
model: modelRef.model,
|
|
12834
|
+
providerId: modelRef.providerId,
|
|
12739
12835
|
mcp,
|
|
12740
12836
|
skillDirs: roleDirs(worktree, run.kind),
|
|
12741
12837
|
// Закрытые дрону пути имеют смысл только при теневом HOME: без него дрон и так
|
|
@@ -14451,9 +14547,10 @@ switch (cmd) {
|
|
|
14451
14547
|
runLogsDir: RUN_LOGS_DIR,
|
|
14452
14548
|
// TASWM-419: каталог лога — по стабильному ключу прогона (тот же адрес, что у демона).
|
|
14453
14549
|
runLogKeys: (r) => db.runLogKeys(r.id),
|
|
14454
|
-
//
|
|
14455
|
-
//
|
|
14456
|
-
|
|
14550
|
+
// TASWM-592: исполнитель НЕ пинится — лаунчер резолвит его по модели ПРОГОНА, тем же
|
|
14551
|
+
// правилом, что и живой демон («какой харнесс» теперь свойство модели, не машины).
|
|
14552
|
+
// Живой настройки роя тут нет (демон молчит — иначе мы сюда не попали бы), поэтому
|
|
14553
|
+
// каскад короче: пин прогона → env машины → дефолт реестра.
|
|
14457
14554
|
droneIsolation: DRONE_ISOLATION,
|
|
14458
14555
|
// TASWM-450: ПОТОЛОК ТРАТ обязателен и здесь. Демон собирает лаунчер с
|
|
14459
14556
|
// `maxCostUsd: () => lastMaxCostUsd`, а этот путь его не передавал вовсе — значит
|
|
@@ -14477,6 +14574,9 @@ switch (cmd) {
|
|
|
14477
14574
|
db,
|
|
14478
14575
|
isAlive: pidAlive,
|
|
14479
14576
|
// тот же kill(pid,0)-гейт liveness, что у демона (procCleanup.ts)
|
|
14577
|
+
// TASWM-592 (ревью): сессию ищет харнесс ПРОГОНА, а не глобальный исполнитель —
|
|
14578
|
+
// иначе побудка codex-прогона при мёртвом демоне теряла продолжаемую сессию.
|
|
14579
|
+
hasTranscript: (run2) => !!run2.worktree_path && executorForRun(run2, null).session.exists(run2.worktree_path),
|
|
14480
14580
|
respawn: (r, mode) => respawnRun(
|
|
14481
14581
|
{ db, launch, repoFallback: SWARM_REPO, worktreesDir: effectiveWorktreesDir(), getTask: () => Promise.resolve(null) },
|
|
14482
14582
|
r,
|