@taskless-app/swarm 0.6.13 → 0.6.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +137 -31
- package/dist/overmind.js +165 -37
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -452,6 +452,40 @@ var init_mergeGate = __esm({
|
|
|
452
452
|
}
|
|
453
453
|
});
|
|
454
454
|
|
|
455
|
+
// ../swarm-core/src/modelPolicy.ts
|
|
456
|
+
function findProvider(id) {
|
|
457
|
+
const key = typeof id === "string" ? id.trim().toLowerCase() : "";
|
|
458
|
+
return MODEL_PROVIDERS.find((p) => p.id === key) ?? null;
|
|
459
|
+
}
|
|
460
|
+
function parseModelRef(ref) {
|
|
461
|
+
const s = typeof ref === "string" ? ref.trim() : "";
|
|
462
|
+
if (!s) return null;
|
|
463
|
+
const slash = s.indexOf("/");
|
|
464
|
+
if (slash < 0) return { providerId: DEFAULT_PROVIDER_ID, model: s };
|
|
465
|
+
const providerId = s.slice(0, slash).trim().toLowerCase();
|
|
466
|
+
const model = s.slice(slash + 1).trim();
|
|
467
|
+
if (!model) return null;
|
|
468
|
+
return { providerId: providerId || DEFAULT_PROVIDER_ID, model };
|
|
469
|
+
}
|
|
470
|
+
function defaultModelFor(role, providerId = null) {
|
|
471
|
+
const provider = findProvider(providerId) ?? findProvider(DEFAULT_PROVIDER_ID);
|
|
472
|
+
return provider.roles[role];
|
|
473
|
+
}
|
|
474
|
+
var MODEL_PROVIDERS, DEFAULT_PROVIDER_ID;
|
|
475
|
+
var init_modelPolicy = __esm({
|
|
476
|
+
"../swarm-core/src/modelPolicy.ts"() {
|
|
477
|
+
"use strict";
|
|
478
|
+
MODEL_PROVIDERS = [
|
|
479
|
+
{
|
|
480
|
+
id: "anthropic",
|
|
481
|
+
label: "Anthropic",
|
|
482
|
+
roles: { executor: "claude-opus-5", review: "claude-sonnet-5" }
|
|
483
|
+
}
|
|
484
|
+
];
|
|
485
|
+
DEFAULT_PROVIDER_ID = MODEL_PROVIDERS[0].id;
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
|
|
455
489
|
// ../swarm-core/src/status.ts
|
|
456
490
|
function statusMeta(status) {
|
|
457
491
|
return STATUS_META[status] ?? FALLBACK;
|
|
@@ -687,6 +721,13 @@ var init_baseBranch = __esm({
|
|
|
687
721
|
});
|
|
688
722
|
|
|
689
723
|
// ../swarm-core/src/hosts.ts
|
|
724
|
+
function hostSignInHint(webUrl) {
|
|
725
|
+
const url = (webUrl ?? "").trim();
|
|
726
|
+
return url ? `\u0420\u043E\u0439 \u043D\u0435 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u043E\u0432\u0430\u043D. \u041E\u0442\u043A\u0440\u043E\u0439\u0442\u0435 ${url} \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435 \u0440\u043E\u044F \u0438 \u0432\u043E\u0439\u0434\u0438\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 Taskless` : "\u0420\u043E\u0439 \u043D\u0435 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u043E\u0432\u0430\u043D. \u041E\u0442\u043A\u0440\u043E\u0439\u0442\u0435 \u0432\u0435\u0431-\u043C\u043E\u0440\u0434\u0443 \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435 \u0440\u043E\u044F \u0438 \u0432\u043E\u0439\u0434\u0438\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 Taskless";
|
|
727
|
+
}
|
|
728
|
+
function hostNotSignedIn(host) {
|
|
729
|
+
return host.signedIn === false;
|
|
730
|
+
}
|
|
690
731
|
function normalizeHostRef(value) {
|
|
691
732
|
if (value == null) return null;
|
|
692
733
|
const trimmed = value.trim();
|
|
@@ -720,6 +761,10 @@ function groupByHost(items, ref, opts = {}) {
|
|
|
720
761
|
offline: false,
|
|
721
762
|
paused: unknown ? false : info?.paused === true,
|
|
722
763
|
pauseReason: unknown ? null : info?.pauseReason ?? null,
|
|
764
|
+
// Поле ставим, ТОЛЬКО если машина о входе сказала: undefined здесь — «не знаем»,
|
|
765
|
+
// и превращать молчание в отказ значило бы объявить невойденным весь парк машин
|
|
766
|
+
// со старым демоном.
|
|
767
|
+
...unknown || info?.signedIn == null ? {} : { signedIn: info.signedIn },
|
|
723
768
|
runs: []
|
|
724
769
|
};
|
|
725
770
|
byKey.set(key, g);
|
|
@@ -752,6 +797,7 @@ function hostStateText(host, nowMs = Date.now(), staleMs2 = HOST_HEARTBEAT_STALE
|
|
|
752
797
|
const at = Date.parse(host.lastSeenAt);
|
|
753
798
|
if (Number.isNaN(at)) return "\u043F\u0443\u043B\u044C\u0441\u0430 \u043D\u0435 \u0431\u044B\u043B\u043E";
|
|
754
799
|
if (!isHostOffline(host.lastSeenAt, nowMs, staleMs2)) {
|
|
800
|
+
if (hostNotSignedIn(host)) return hostSignInHint(host.webUrl);
|
|
755
801
|
if (host.paused) return host.pauseReason ? `\u043D\u0430 \u043F\u0430\u0443\u0437\u0435 \u2014 ${host.pauseReason}` : "\u043D\u0430 \u043F\u0430\u0443\u0437\u0435";
|
|
756
802
|
if (host.storm) return host.stormReason ? `\u0448\u0442\u043E\u0440\u043C\u0438\u0442 \u2014 ${host.stormReason}` : "\u0448\u0442\u043E\u0440\u043C\u0438\u0442 \u2014 \u043D\u043E\u0432\u044B\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u0442";
|
|
757
803
|
return "\u043D\u0430 \u0441\u0432\u044F\u0437\u0438";
|
|
@@ -873,6 +919,42 @@ var init_cost = __esm({
|
|
|
873
919
|
}
|
|
874
920
|
});
|
|
875
921
|
|
|
922
|
+
// ../swarm-core/src/modelPricing.ts
|
|
923
|
+
function findModelPricing(model, table = MODEL_PRICING) {
|
|
924
|
+
if (!model) return void 0;
|
|
925
|
+
const exact = table[model];
|
|
926
|
+
if (exact) return exact;
|
|
927
|
+
let bestKey = "";
|
|
928
|
+
for (const key of Object.keys(table)) {
|
|
929
|
+
if (model.startsWith(key) && key.length > bestKey.length) bestKey = key;
|
|
930
|
+
}
|
|
931
|
+
return bestKey ? table[bestKey] : void 0;
|
|
932
|
+
}
|
|
933
|
+
var MODEL_PRICING, CACHE_READ_FACTOR, CACHE_WRITE_5M_FACTOR;
|
|
934
|
+
var init_modelPricing = __esm({
|
|
935
|
+
"../swarm-core/src/modelPricing.ts"() {
|
|
936
|
+
"use strict";
|
|
937
|
+
MODEL_PRICING = {
|
|
938
|
+
// Sonnet 5 — 2/10. Цена ПОСТОЯННАЯ: 18.08.2026 владелец подтвердил, что вводной
|
|
939
|
+
// она быть перестала. Даты годности у тарифа тут больше нет и заводить её заново не
|
|
940
|
+
// нужно: тариф со сроком означает, что в календарный день расчёт молча меняет цифру,
|
|
941
|
+
// и «почему прогон вчера стоил иначе» приходится выяснять по коду, а не по таблице.
|
|
942
|
+
// Меняется цена — меняется строка, одним видимым коммитом.
|
|
943
|
+
"claude-sonnet-5": { inputPerMTok: 2, outputPerMTok: 10 },
|
|
944
|
+
"claude-opus-5": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
945
|
+
"claude-fable-5": { inputPerMTok: 10, outputPerMTok: 50 },
|
|
946
|
+
// Прогоны и вызовы на 4.x остаются в истории — строки держим ради их стоимости.
|
|
947
|
+
"claude-opus-4-8": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
948
|
+
"claude-opus-4-7": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
949
|
+
"claude-opus-4-6": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
950
|
+
"claude-sonnet-4-6": { inputPerMTok: 3, outputPerMTok: 15 },
|
|
951
|
+
"claude-haiku-4-5": { inputPerMTok: 1, outputPerMTok: 5 }
|
|
952
|
+
};
|
|
953
|
+
CACHE_READ_FACTOR = 0.1;
|
|
954
|
+
CACHE_WRITE_5M_FACTOR = 1.25;
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
|
|
876
958
|
// ../swarm-core/src/runGuards.ts
|
|
877
959
|
function remainingRunBudgetUsd(maxCostUsd, spentUsd) {
|
|
878
960
|
if (!(maxCostUsd > 0)) return null;
|
|
@@ -1316,6 +1398,7 @@ var init_src = __esm({
|
|
|
1316
1398
|
init_reviewPhase();
|
|
1317
1399
|
init_mergeGate();
|
|
1318
1400
|
init_verifyVerdict();
|
|
1401
|
+
init_modelPolicy();
|
|
1319
1402
|
init_status();
|
|
1320
1403
|
init_deps();
|
|
1321
1404
|
init_time();
|
|
@@ -1331,6 +1414,7 @@ var init_src = __esm({
|
|
|
1331
1414
|
init_hostCommands();
|
|
1332
1415
|
init_relief();
|
|
1333
1416
|
init_cost();
|
|
1417
|
+
init_modelPricing();
|
|
1334
1418
|
init_runGuards();
|
|
1335
1419
|
init_codeZones();
|
|
1336
1420
|
init_effort();
|
|
@@ -1428,18 +1512,12 @@ var init_swarmFiles = __esm({
|
|
|
1428
1512
|
});
|
|
1429
1513
|
|
|
1430
1514
|
// src/pricing.ts
|
|
1431
|
-
function
|
|
1432
|
-
const
|
|
1433
|
-
if (!intro) return pricing;
|
|
1434
|
-
const day = at.toISOString().slice(0, 10);
|
|
1435
|
-
return day <= intro.until ? { inputPerMTok: intro.inputPerMTok, outputPerMTok: intro.outputPerMTok } : pricing;
|
|
1436
|
-
}
|
|
1437
|
-
function costFromUsage(usage2, model, table = MODEL_PRICING, at = /* @__PURE__ */ new Date()) {
|
|
1438
|
-
const entry = model ? table[model] : void 0;
|
|
1515
|
+
function costFromUsage(usage2, model, table = MODEL_PRICING) {
|
|
1516
|
+
const entry = findModelPricing(model, table);
|
|
1439
1517
|
if (!entry) {
|
|
1440
1518
|
return { costUsd: 0, warning: `\u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u043C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u0430\u0439\u0441\u0438\u043D\u0433\u0430: "${model ?? ""}" \u2014 cost \u043D\u0435 \u043F\u043E\u0441\u0447\u0438\u0442\u0430\u043D` };
|
|
1441
1519
|
}
|
|
1442
|
-
const pricing =
|
|
1520
|
+
const pricing = entry;
|
|
1443
1521
|
const input = usage2.input_tokens ?? 0;
|
|
1444
1522
|
const output = usage2.output_tokens ?? 0;
|
|
1445
1523
|
const cacheWrite = usage2.cache_creation_input_tokens ?? 0;
|
|
@@ -1447,24 +1525,10 @@ function costFromUsage(usage2, model, table = MODEL_PRICING, at = /* @__PURE__ *
|
|
|
1447
1525
|
const costUsd = (input * pricing.inputPerMTok + output * pricing.outputPerMTok + cacheWrite * pricing.inputPerMTok * CACHE_WRITE_5M_FACTOR + cacheRead * pricing.inputPerMTok * CACHE_READ_FACTOR) / 1e6;
|
|
1448
1526
|
return { costUsd };
|
|
1449
1527
|
}
|
|
1450
|
-
var MODEL_PRICING, CACHE_READ_FACTOR, CACHE_WRITE_5M_FACTOR;
|
|
1451
1528
|
var init_pricing = __esm({
|
|
1452
1529
|
"src/pricing.ts"() {
|
|
1453
1530
|
"use strict";
|
|
1454
|
-
|
|
1455
|
-
// Sonnet 5: вводные 2/10 по 31.08.2026 включительно, дальше базовые 3/15.
|
|
1456
|
-
"claude-sonnet-5": {
|
|
1457
|
-
inputPerMTok: 3,
|
|
1458
|
-
outputPerMTok: 15,
|
|
1459
|
-
intro: { inputPerMTok: 2, outputPerMTok: 10, until: "2026-08-31" }
|
|
1460
|
-
},
|
|
1461
|
-
"claude-opus-5": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
1462
|
-
// Прогоны на 4.8 остаются в истории — строку держим ради их стоимости.
|
|
1463
|
-
"claude-opus-4-8": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
1464
|
-
"claude-haiku-4-5": { inputPerMTok: 1, outputPerMTok: 5 }
|
|
1465
|
-
};
|
|
1466
|
-
CACHE_READ_FACTOR = 0.1;
|
|
1467
|
-
CACHE_WRITE_5M_FACTOR = 1.25;
|
|
1531
|
+
init_src();
|
|
1468
1532
|
}
|
|
1469
1533
|
});
|
|
1470
1534
|
|
|
@@ -2103,7 +2167,7 @@ var init_package = __esm({
|
|
|
2103
2167
|
"package.json"() {
|
|
2104
2168
|
package_default = {
|
|
2105
2169
|
name: "@taskless-app/swarm",
|
|
2106
|
-
version: "0.6.
|
|
2170
|
+
version: "0.6.14",
|
|
2107
2171
|
type: "module",
|
|
2108
2172
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
2109
2173
|
keywords: [
|
|
@@ -2153,7 +2217,7 @@ var init_package = __esm({
|
|
|
2153
2217
|
execa: "^10.0.0",
|
|
2154
2218
|
zod: "3.25.76"
|
|
2155
2219
|
},
|
|
2156
|
-
gitHead: "
|
|
2220
|
+
gitHead: "a3b11498dbed5db3642fe4fc83bcd2054efde055"
|
|
2157
2221
|
};
|
|
2158
2222
|
}
|
|
2159
2223
|
});
|
|
@@ -2677,6 +2741,7 @@ function rowToRun(row) {
|
|
|
2677
2741
|
area: row.area ?? null,
|
|
2678
2742
|
depends_on: row.depends_on ? JSON.parse(row.depends_on) : [],
|
|
2679
2743
|
model: row.model ?? null,
|
|
2744
|
+
review_model: row.review_model ?? null,
|
|
2680
2745
|
// TASWM-395: уровень усилия — через toEffortLevel, а не сырой строкой: в зеркале может
|
|
2681
2746
|
// лежать значение, записанное демоном другой версии (словарь уровней задаёт CLI), и
|
|
2682
2747
|
// непонятное здесь означает «уровень не задан», а не сломанный тип.
|
|
@@ -2754,6 +2819,7 @@ CREATE TABLE IF NOT EXISTS runs (
|
|
|
2754
2819
|
area TEXT,
|
|
2755
2820
|
depends_on TEXT,
|
|
2756
2821
|
model TEXT,
|
|
2822
|
+
review_model TEXT,
|
|
2757
2823
|
effort TEXT,
|
|
2758
2824
|
cost_usd REAL NOT NULL DEFAULT 0,
|
|
2759
2825
|
tokens_in INTEGER NOT NULL DEFAULT 0,
|
|
@@ -2867,7 +2933,11 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
2867
2933
|
"ALTER TABLE runs ADD COLUMN duration_ms INTEGER NOT NULL DEFAULT 0",
|
|
2868
2934
|
// TASWM-395: усилие рассуждения прогона (`claude --effort`). Nullable без DEFAULT —
|
|
2869
2935
|
// NULL это и все прогоны до фичи, и прогоны на дефолте харнесса.
|
|
2870
|
-
"ALTER TABLE runs ADD COLUMN effort TEXT"
|
|
2936
|
+
"ALTER TABLE runs ADD COLUMN effort TEXT",
|
|
2937
|
+
// TASWM-451: чем проверять работу этого прогона — модель ревью, приехавшая с задачи.
|
|
2938
|
+
// Живёт на прогоне ИСПОЛНИТЕЛЯ: ревьюер и верификатор заводятся локально и читают её
|
|
2939
|
+
// оттуда, а не ходят за задачей в прод в момент спавна.
|
|
2940
|
+
"ALTER TABLE runs ADD COLUMN review_model TEXT"
|
|
2871
2941
|
]) {
|
|
2872
2942
|
try {
|
|
2873
2943
|
this.db.exec(alter);
|
|
@@ -2887,7 +2957,7 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
2887
2957
|
createRun(input) {
|
|
2888
2958
|
const name = input.name ?? pickName(this.db.prepare("SELECT id, name, status FROM runs").all());
|
|
2889
2959
|
const res = this.db.prepare(
|
|
2890
|
-
"INSERT INTO runs (task_slug, name, kind, hero_run_id, repo, area, depends_on, model, effort, decision, resolves_run_id, reviews_run_id, verifies_run_id, review_cycle, priority, base_branch, workspace_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
2960
|
+
"INSERT INTO runs (task_slug, name, kind, hero_run_id, repo, area, depends_on, model, review_model, effort, decision, resolves_run_id, reviews_run_id, verifies_run_id, review_cycle, priority, base_branch, workspace_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
2891
2961
|
).run(
|
|
2892
2962
|
input.task_slug,
|
|
2893
2963
|
name,
|
|
@@ -2900,6 +2970,7 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
2900
2970
|
input.area ?? null,
|
|
2901
2971
|
input.depends_on && input.depends_on.length ? JSON.stringify(input.depends_on) : null,
|
|
2902
2972
|
input.model ?? null,
|
|
2973
|
+
input.review_model ?? null,
|
|
2903
2974
|
input.effort ?? null,
|
|
2904
2975
|
// усилие рассуждения (TASWM-395); null → разрешит спавн
|
|
2905
2976
|
input.decision ? JSON.stringify(input.decision) : null,
|
|
@@ -5390,6 +5461,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
5390
5461
|
if (patch.status === "running") out.executor = this.executorId;
|
|
5391
5462
|
if ("area" in patch) out.area = patch.area ?? null;
|
|
5392
5463
|
if ("model" in patch) out.model = patch.model ?? null;
|
|
5464
|
+
if ("review_model" in patch) out.reviewModel = patch.review_model ?? null;
|
|
5393
5465
|
if ("effort" in patch) out.effort = patch.effort ?? null;
|
|
5394
5466
|
if ("name" in patch) out.droneName = patch.name ?? null;
|
|
5395
5467
|
if ("cost_usd" in patch && patch.cost_usd !== void 0) out.costUsd = patch.cost_usd;
|
|
@@ -5463,6 +5535,10 @@ var SwarmClient = class _SwarmClient {
|
|
|
5463
5535
|
area: input.area ?? null,
|
|
5464
5536
|
depends_on: input.depends_on && input.depends_on.length ? input.depends_on : [],
|
|
5465
5537
|
model: input.model ?? null,
|
|
5538
|
+
// TASWM-451: чем проверять эту работу. У прогона исполнителя приезжает из задачи
|
|
5539
|
+
// (claim в проде), у локально заведённого ревьюера/верификатора — наследством от
|
|
5540
|
+
// проверяемого прогона, которое ставит spawnReviewer/spawnVerifier.
|
|
5541
|
+
review_model: input.review_model ?? null,
|
|
5466
5542
|
// Усилие рассуждения (TASWM-395): пин, если его поставили на спавне; иначе null —
|
|
5467
5543
|
// разрешённый уровень впечатает launcher, когда действительно поднимет дрона.
|
|
5468
5544
|
effort: input.effort ?? null,
|
|
@@ -5521,6 +5597,8 @@ var SwarmClient = class _SwarmClient {
|
|
|
5521
5597
|
status: "queued",
|
|
5522
5598
|
area: d.area,
|
|
5523
5599
|
model: d.model,
|
|
5600
|
+
review_model: d.review_model,
|
|
5601
|
+
// TASWM-451: чем проверять — едет рядом с моделью
|
|
5524
5602
|
effort: d.effort,
|
|
5525
5603
|
// усилие рассуждения (TASWM-395), если пинили на спавне
|
|
5526
5604
|
name: d.name,
|
|
@@ -6648,7 +6726,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
6648
6726
|
*
|
|
6649
6727
|
* Прод недоступен / ответ без поручений → пустые списки (демон работает дальше).
|
|
6650
6728
|
*/
|
|
6651
|
-
async heartbeat(daemonVersion, pause, capacity, resources, storm) {
|
|
6729
|
+
async heartbeat(daemonVersion, pause, capacity, resources, storm, signIn) {
|
|
6652
6730
|
try {
|
|
6653
6731
|
const args = this.repoFullNames.length ? { repoFullNames: this.repoFullNames } : {};
|
|
6654
6732
|
if (daemonVersion) args.daemonVersion = daemonVersion;
|
|
@@ -6658,6 +6736,10 @@ var SwarmClient = class _SwarmClient {
|
|
|
6658
6736
|
args.paused = !!pause?.paused;
|
|
6659
6737
|
if (pause?.reason) args.pauseReason = pause.reason;
|
|
6660
6738
|
}
|
|
6739
|
+
if (signIn) {
|
|
6740
|
+
args.signedIn = signIn.signedIn;
|
|
6741
|
+
if (signIn.webUrl) args.webUrl = signIn.webUrl;
|
|
6742
|
+
}
|
|
6661
6743
|
if (storm !== void 0 && storm !== null) {
|
|
6662
6744
|
args.storm = storm.active;
|
|
6663
6745
|
if (storm.reason) args.stormReason = storm.reason;
|
|
@@ -6889,6 +6971,8 @@ var SwarmClient = class _SwarmClient {
|
|
|
6889
6971
|
// существующая локальная запись НЕ перетирается (поле не в field-copy ниже),
|
|
6890
6972
|
// в отличие от respawns, — авто-ревью v2, TASWM-162.
|
|
6891
6973
|
review_cycle: 0,
|
|
6974
|
+
// TASWM-451: модель ПРОВЕРКИ — прод-поле, заполняется ниже из ответа (как base_branch).
|
|
6975
|
+
review_model: null,
|
|
6892
6976
|
task_title: null,
|
|
6893
6977
|
task_desc: null,
|
|
6894
6978
|
task_url: null,
|
|
@@ -6941,6 +7025,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
6941
7025
|
local.area = pr.area ?? null;
|
|
6942
7026
|
local.depends_on = Array.isArray(pr.dependsOn) ? pr.dependsOn : [];
|
|
6943
7027
|
local.model = pr.model ?? null;
|
|
7028
|
+
local.review_model = pr.reviewModel ?? null;
|
|
6944
7029
|
local.effort = toEffortLevel(pr.effort);
|
|
6945
7030
|
local.cost_usd = typeof pr.costUsd === "number" ? pr.costUsd : 0;
|
|
6946
7031
|
local.tokens_in = typeof pr.tokensIn === "number" ? pr.tokensIn : 0;
|
|
@@ -9985,12 +10070,26 @@ function pick(v) {
|
|
|
9985
10070
|
const s = typeof v === "string" ? v.trim() : "";
|
|
9986
10071
|
return s.length > 0 ? s : null;
|
|
9987
10072
|
}
|
|
10073
|
+
function isReviewKind(kind) {
|
|
10074
|
+
return kind === "reviewer" || kind === "verifier";
|
|
10075
|
+
}
|
|
10076
|
+
function modelRoleOf(kind) {
|
|
10077
|
+
return isReviewKind(kind) ? "review" : "executor";
|
|
10078
|
+
}
|
|
9988
10079
|
function forKind(kind, swarm, norm) {
|
|
9989
10080
|
if (!swarm) return null;
|
|
9990
|
-
return kind
|
|
10081
|
+
return isReviewKind(kind) ? norm(swarm.reviewer) ?? norm(swarm.drone) : norm(swarm.drone);
|
|
10082
|
+
}
|
|
10083
|
+
function resolveRunModelRef(run, swarm, env = process.env) {
|
|
10084
|
+
const configured = parseModelRef(
|
|
10085
|
+
pick(run.model) ?? forKind(run.kind, swarm, pick) ?? pick(env.SWARM_DRONE_MODEL)
|
|
10086
|
+
);
|
|
10087
|
+
if (configured) return configured;
|
|
10088
|
+
const providerId = pick(env.SWARM_MODEL_PROVIDER) ?? DEFAULT_PROVIDER_ID;
|
|
10089
|
+
return { providerId, model: defaultModelFor(modelRoleOf(run.kind), providerId) };
|
|
9991
10090
|
}
|
|
9992
10091
|
function resolveRunModel(run, swarm, env = process.env) {
|
|
9993
|
-
return
|
|
10092
|
+
return resolveRunModelRef(run, swarm, env).model;
|
|
9994
10093
|
}
|
|
9995
10094
|
function resolveRunEffort(run, swarm, env = process.env) {
|
|
9996
10095
|
return toEffortLevel(run.effort) ?? forKind(run.kind, swarm, toEffortLevel) ?? toEffortLevel(env.SWARM_DRONE_EFFORT) ?? null;
|
|
@@ -11922,6 +12021,13 @@ switch (cmd) {
|
|
|
11922
12021
|
// должен внезапно поднимать дрона другим харнессом.
|
|
11923
12022
|
executor: EXECUTOR,
|
|
11924
12023
|
droneIsolation: DRONE_ISOLATION,
|
|
12024
|
+
// TASWM-450: ПОТОЛОК ТРАТ обязателен и здесь. Демон собирает лаунчер с
|
|
12025
|
+
// `maxCostUsd: () => lastMaxCostUsd`, а этот путь его не передавал вовсе — значит
|
|
12026
|
+
// разбуженный руками прогон уходил БЕЗ `--max-budget-usd` и мог жечь сколько угодно.
|
|
12027
|
+
// Живой настройки роя у CLI нет (демон не отвечает — иначе мы сюда не попали бы),
|
|
12028
|
+
// поэтому источник тот же, что и у остальных ~40 ручек: env машины, а за ним дефолт
|
|
12029
|
+
// ядра. Это ровно то, что делает `launchBudgetUsd(null, …)`.
|
|
12030
|
+
maxCostUsd: () => null,
|
|
11925
12031
|
...DRONE_CHANNEL_ENABLED ? {
|
|
11926
12032
|
channel: {
|
|
11927
12033
|
url: DRONE_CHANNEL_URL,
|
package/dist/overmind.js
CHANGED
|
@@ -425,6 +425,40 @@ var init_mergeGate = __esm({
|
|
|
425
425
|
}
|
|
426
426
|
});
|
|
427
427
|
|
|
428
|
+
// ../swarm-core/src/modelPolicy.ts
|
|
429
|
+
function findProvider(id) {
|
|
430
|
+
const key = typeof id === "string" ? id.trim().toLowerCase() : "";
|
|
431
|
+
return MODEL_PROVIDERS.find((p) => p.id === key) ?? null;
|
|
432
|
+
}
|
|
433
|
+
function parseModelRef(ref) {
|
|
434
|
+
const s = typeof ref === "string" ? ref.trim() : "";
|
|
435
|
+
if (!s) return null;
|
|
436
|
+
const slash = s.indexOf("/");
|
|
437
|
+
if (slash < 0) return { providerId: DEFAULT_PROVIDER_ID, model: s };
|
|
438
|
+
const providerId = s.slice(0, slash).trim().toLowerCase();
|
|
439
|
+
const model = s.slice(slash + 1).trim();
|
|
440
|
+
if (!model) return null;
|
|
441
|
+
return { providerId: providerId || DEFAULT_PROVIDER_ID, model };
|
|
442
|
+
}
|
|
443
|
+
function defaultModelFor(role, providerId = null) {
|
|
444
|
+
const provider = findProvider(providerId) ?? findProvider(DEFAULT_PROVIDER_ID);
|
|
445
|
+
return provider.roles[role];
|
|
446
|
+
}
|
|
447
|
+
var MODEL_PROVIDERS, DEFAULT_PROVIDER_ID;
|
|
448
|
+
var init_modelPolicy = __esm({
|
|
449
|
+
"../swarm-core/src/modelPolicy.ts"() {
|
|
450
|
+
"use strict";
|
|
451
|
+
MODEL_PROVIDERS = [
|
|
452
|
+
{
|
|
453
|
+
id: "anthropic",
|
|
454
|
+
label: "Anthropic",
|
|
455
|
+
roles: { executor: "claude-opus-5", review: "claude-sonnet-5" }
|
|
456
|
+
}
|
|
457
|
+
];
|
|
458
|
+
DEFAULT_PROVIDER_ID = MODEL_PROVIDERS[0].id;
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
|
|
428
462
|
// ../swarm-core/src/status.ts
|
|
429
463
|
function isAwaitingCi(status, stopReason) {
|
|
430
464
|
return status === "paused" && stopReason === AWAITING_CI_REASON;
|
|
@@ -989,6 +1023,42 @@ var init_cost = __esm({
|
|
|
989
1023
|
}
|
|
990
1024
|
});
|
|
991
1025
|
|
|
1026
|
+
// ../swarm-core/src/modelPricing.ts
|
|
1027
|
+
function findModelPricing(model, table = MODEL_PRICING) {
|
|
1028
|
+
if (!model) return void 0;
|
|
1029
|
+
const exact = table[model];
|
|
1030
|
+
if (exact) return exact;
|
|
1031
|
+
let bestKey = "";
|
|
1032
|
+
for (const key of Object.keys(table)) {
|
|
1033
|
+
if (model.startsWith(key) && key.length > bestKey.length) bestKey = key;
|
|
1034
|
+
}
|
|
1035
|
+
return bestKey ? table[bestKey] : void 0;
|
|
1036
|
+
}
|
|
1037
|
+
var MODEL_PRICING, CACHE_READ_FACTOR, CACHE_WRITE_5M_FACTOR;
|
|
1038
|
+
var init_modelPricing = __esm({
|
|
1039
|
+
"../swarm-core/src/modelPricing.ts"() {
|
|
1040
|
+
"use strict";
|
|
1041
|
+
MODEL_PRICING = {
|
|
1042
|
+
// Sonnet 5 — 2/10. Цена ПОСТОЯННАЯ: 18.08.2026 владелец подтвердил, что вводной
|
|
1043
|
+
// она быть перестала. Даты годности у тарифа тут больше нет и заводить её заново не
|
|
1044
|
+
// нужно: тариф со сроком означает, что в календарный день расчёт молча меняет цифру,
|
|
1045
|
+
// и «почему прогон вчера стоил иначе» приходится выяснять по коду, а не по таблице.
|
|
1046
|
+
// Меняется цена — меняется строка, одним видимым коммитом.
|
|
1047
|
+
"claude-sonnet-5": { inputPerMTok: 2, outputPerMTok: 10 },
|
|
1048
|
+
"claude-opus-5": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
1049
|
+
"claude-fable-5": { inputPerMTok: 10, outputPerMTok: 50 },
|
|
1050
|
+
// Прогоны и вызовы на 4.x остаются в истории — строки держим ради их стоимости.
|
|
1051
|
+
"claude-opus-4-8": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
1052
|
+
"claude-opus-4-7": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
1053
|
+
"claude-opus-4-6": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
1054
|
+
"claude-sonnet-4-6": { inputPerMTok: 3, outputPerMTok: 15 },
|
|
1055
|
+
"claude-haiku-4-5": { inputPerMTok: 1, outputPerMTok: 5 }
|
|
1056
|
+
};
|
|
1057
|
+
CACHE_READ_FACTOR = 0.1;
|
|
1058
|
+
CACHE_WRITE_5M_FACTOR = 1.25;
|
|
1059
|
+
}
|
|
1060
|
+
});
|
|
1061
|
+
|
|
992
1062
|
// ../swarm-core/src/runGuards.ts
|
|
993
1063
|
function remainingRunBudgetUsd(maxCostUsd, spentUsd) {
|
|
994
1064
|
if (!(maxCostUsd > 0)) return null;
|
|
@@ -1448,6 +1518,7 @@ var init_src = __esm({
|
|
|
1448
1518
|
init_reviewPhase();
|
|
1449
1519
|
init_mergeGate();
|
|
1450
1520
|
init_verifyVerdict();
|
|
1521
|
+
init_modelPolicy();
|
|
1451
1522
|
init_status();
|
|
1452
1523
|
init_deps();
|
|
1453
1524
|
init_time();
|
|
@@ -1463,6 +1534,7 @@ var init_src = __esm({
|
|
|
1463
1534
|
init_hostCommands();
|
|
1464
1535
|
init_relief();
|
|
1465
1536
|
init_cost();
|
|
1537
|
+
init_modelPricing();
|
|
1466
1538
|
init_runGuards();
|
|
1467
1539
|
init_codeZones();
|
|
1468
1540
|
init_effort();
|
|
@@ -1597,18 +1669,12 @@ var init_swarmFiles = __esm({
|
|
|
1597
1669
|
});
|
|
1598
1670
|
|
|
1599
1671
|
// src/pricing.ts
|
|
1600
|
-
function
|
|
1601
|
-
const
|
|
1602
|
-
if (!intro) return pricing;
|
|
1603
|
-
const day = at.toISOString().slice(0, 10);
|
|
1604
|
-
return day <= intro.until ? { inputPerMTok: intro.inputPerMTok, outputPerMTok: intro.outputPerMTok } : pricing;
|
|
1605
|
-
}
|
|
1606
|
-
function costFromUsage(usage, model, table = MODEL_PRICING, at = /* @__PURE__ */ new Date()) {
|
|
1607
|
-
const entry = model ? table[model] : void 0;
|
|
1672
|
+
function costFromUsage(usage, model, table = MODEL_PRICING) {
|
|
1673
|
+
const entry = findModelPricing(model, table);
|
|
1608
1674
|
if (!entry) {
|
|
1609
1675
|
return { costUsd: 0, warning: `\u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u043C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u0430\u0439\u0441\u0438\u043D\u0433\u0430: "${model ?? ""}" \u2014 cost \u043D\u0435 \u043F\u043E\u0441\u0447\u0438\u0442\u0430\u043D` };
|
|
1610
1676
|
}
|
|
1611
|
-
const pricing =
|
|
1677
|
+
const pricing = entry;
|
|
1612
1678
|
const input = usage.input_tokens ?? 0;
|
|
1613
1679
|
const output = usage.output_tokens ?? 0;
|
|
1614
1680
|
const cacheWrite = usage.cache_creation_input_tokens ?? 0;
|
|
@@ -1616,24 +1682,10 @@ function costFromUsage(usage, model, table = MODEL_PRICING, at = /* @__PURE__ */
|
|
|
1616
1682
|
const costUsd = (input * pricing.inputPerMTok + output * pricing.outputPerMTok + cacheWrite * pricing.inputPerMTok * CACHE_WRITE_5M_FACTOR + cacheRead * pricing.inputPerMTok * CACHE_READ_FACTOR) / 1e6;
|
|
1617
1683
|
return { costUsd };
|
|
1618
1684
|
}
|
|
1619
|
-
var MODEL_PRICING, CACHE_READ_FACTOR, CACHE_WRITE_5M_FACTOR;
|
|
1620
1685
|
var init_pricing = __esm({
|
|
1621
1686
|
"src/pricing.ts"() {
|
|
1622
1687
|
"use strict";
|
|
1623
|
-
|
|
1624
|
-
// Sonnet 5: вводные 2/10 по 31.08.2026 включительно, дальше базовые 3/15.
|
|
1625
|
-
"claude-sonnet-5": {
|
|
1626
|
-
inputPerMTok: 3,
|
|
1627
|
-
outputPerMTok: 15,
|
|
1628
|
-
intro: { inputPerMTok: 2, outputPerMTok: 10, until: "2026-08-31" }
|
|
1629
|
-
},
|
|
1630
|
-
"claude-opus-5": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
1631
|
-
// Прогоны на 4.8 остаются в истории — строку держим ради их стоимости.
|
|
1632
|
-
"claude-opus-4-8": { inputPerMTok: 5, outputPerMTok: 25 },
|
|
1633
|
-
"claude-haiku-4-5": { inputPerMTok: 1, outputPerMTok: 5 }
|
|
1634
|
-
};
|
|
1635
|
-
CACHE_READ_FACTOR = 0.1;
|
|
1636
|
-
CACHE_WRITE_5M_FACTOR = 1.25;
|
|
1688
|
+
init_src();
|
|
1637
1689
|
}
|
|
1638
1690
|
});
|
|
1639
1691
|
|
|
@@ -4361,6 +4413,10 @@ function authStateLine(nowMs = Date.now()) {
|
|
|
4361
4413
|
if (!denial) return null;
|
|
4362
4414
|
return `\u0414\u041E\u0421\u0422\u0423\u041F \u041A TASKLESS \u041E\u0422\u041E\u0417\u0412\u0410\u041D \u2014 ${deniedSinceLine(nowMs)}. \u041F\u043E\u0447\u0438\u043D\u0438 \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435: swarm login`;
|
|
4363
4415
|
}
|
|
4416
|
+
function signedInToTaskless() {
|
|
4417
|
+
if (denial) return false;
|
|
4418
|
+
return Boolean(loadAgentToken()?.token);
|
|
4419
|
+
}
|
|
4364
4420
|
function deniedSinceLine(nowMs) {
|
|
4365
4421
|
const d = denial;
|
|
4366
4422
|
if (!d) return "";
|
|
@@ -4924,6 +4980,7 @@ function rowToRun(row) {
|
|
|
4924
4980
|
area: row.area ?? null,
|
|
4925
4981
|
depends_on: row.depends_on ? JSON.parse(row.depends_on) : [],
|
|
4926
4982
|
model: row.model ?? null,
|
|
4983
|
+
review_model: row.review_model ?? null,
|
|
4927
4984
|
// TASWM-395: уровень усилия — через toEffortLevel, а не сырой строкой: в зеркале может
|
|
4928
4985
|
// лежать значение, записанное демоном другой версии (словарь уровней задаёт CLI), и
|
|
4929
4986
|
// непонятное здесь означает «уровень не задан», а не сломанный тип.
|
|
@@ -5001,6 +5058,7 @@ CREATE TABLE IF NOT EXISTS runs (
|
|
|
5001
5058
|
area TEXT,
|
|
5002
5059
|
depends_on TEXT,
|
|
5003
5060
|
model TEXT,
|
|
5061
|
+
review_model TEXT,
|
|
5004
5062
|
effort TEXT,
|
|
5005
5063
|
cost_usd REAL NOT NULL DEFAULT 0,
|
|
5006
5064
|
tokens_in INTEGER NOT NULL DEFAULT 0,
|
|
@@ -5114,7 +5172,11 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
5114
5172
|
"ALTER TABLE runs ADD COLUMN duration_ms INTEGER NOT NULL DEFAULT 0",
|
|
5115
5173
|
// TASWM-395: усилие рассуждения прогона (`claude --effort`). Nullable без DEFAULT —
|
|
5116
5174
|
// NULL это и все прогоны до фичи, и прогоны на дефолте харнесса.
|
|
5117
|
-
"ALTER TABLE runs ADD COLUMN effort TEXT"
|
|
5175
|
+
"ALTER TABLE runs ADD COLUMN effort TEXT",
|
|
5176
|
+
// TASWM-451: чем проверять работу этого прогона — модель ревью, приехавшая с задачи.
|
|
5177
|
+
// Живёт на прогоне ИСПОЛНИТЕЛЯ: ревьюер и верификатор заводятся локально и читают её
|
|
5178
|
+
// оттуда, а не ходят за задачей в прод в момент спавна.
|
|
5179
|
+
"ALTER TABLE runs ADD COLUMN review_model TEXT"
|
|
5118
5180
|
]) {
|
|
5119
5181
|
try {
|
|
5120
5182
|
this.db.exec(alter);
|
|
@@ -5134,7 +5196,7 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
5134
5196
|
createRun(input) {
|
|
5135
5197
|
const name = input.name ?? pickName(this.db.prepare("SELECT id, name, status FROM runs").all());
|
|
5136
5198
|
const res = this.db.prepare(
|
|
5137
|
-
"INSERT INTO runs (task_slug, name, kind, hero_run_id, repo, area, depends_on, model, effort, decision, resolves_run_id, reviews_run_id, verifies_run_id, review_cycle, priority, base_branch, workspace_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
5199
|
+
"INSERT INTO runs (task_slug, name, kind, hero_run_id, repo, area, depends_on, model, review_model, effort, decision, resolves_run_id, reviews_run_id, verifies_run_id, review_cycle, priority, base_branch, workspace_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
5138
5200
|
).run(
|
|
5139
5201
|
input.task_slug,
|
|
5140
5202
|
name,
|
|
@@ -5147,6 +5209,7 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
5147
5209
|
input.area ?? null,
|
|
5148
5210
|
input.depends_on && input.depends_on.length ? JSON.stringify(input.depends_on) : null,
|
|
5149
5211
|
input.model ?? null,
|
|
5212
|
+
input.review_model ?? null,
|
|
5150
5213
|
input.effort ?? null,
|
|
5151
5214
|
// усилие рассуждения (TASWM-395); null → разрешит спавн
|
|
5152
5215
|
input.decision ? JSON.stringify(input.decision) : null,
|
|
@@ -6306,6 +6369,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
6306
6369
|
if (patch.status === "running") out.executor = this.executorId;
|
|
6307
6370
|
if ("area" in patch) out.area = patch.area ?? null;
|
|
6308
6371
|
if ("model" in patch) out.model = patch.model ?? null;
|
|
6372
|
+
if ("review_model" in patch) out.reviewModel = patch.review_model ?? null;
|
|
6309
6373
|
if ("effort" in patch) out.effort = patch.effort ?? null;
|
|
6310
6374
|
if ("name" in patch) out.droneName = patch.name ?? null;
|
|
6311
6375
|
if ("cost_usd" in patch && patch.cost_usd !== void 0) out.costUsd = patch.cost_usd;
|
|
@@ -6379,6 +6443,10 @@ var SwarmClient = class _SwarmClient {
|
|
|
6379
6443
|
area: input.area ?? null,
|
|
6380
6444
|
depends_on: input.depends_on && input.depends_on.length ? input.depends_on : [],
|
|
6381
6445
|
model: input.model ?? null,
|
|
6446
|
+
// TASWM-451: чем проверять эту работу. У прогона исполнителя приезжает из задачи
|
|
6447
|
+
// (claim в проде), у локально заведённого ревьюера/верификатора — наследством от
|
|
6448
|
+
// проверяемого прогона, которое ставит spawnReviewer/spawnVerifier.
|
|
6449
|
+
review_model: input.review_model ?? null,
|
|
6382
6450
|
// Усилие рассуждения (TASWM-395): пин, если его поставили на спавне; иначе null —
|
|
6383
6451
|
// разрешённый уровень впечатает launcher, когда действительно поднимет дрона.
|
|
6384
6452
|
effort: input.effort ?? null,
|
|
@@ -6437,6 +6505,8 @@ var SwarmClient = class _SwarmClient {
|
|
|
6437
6505
|
status: "queued",
|
|
6438
6506
|
area: d.area,
|
|
6439
6507
|
model: d.model,
|
|
6508
|
+
review_model: d.review_model,
|
|
6509
|
+
// TASWM-451: чем проверять — едет рядом с моделью
|
|
6440
6510
|
effort: d.effort,
|
|
6441
6511
|
// усилие рассуждения (TASWM-395), если пинили на спавне
|
|
6442
6512
|
name: d.name,
|
|
@@ -7564,7 +7634,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
7564
7634
|
*
|
|
7565
7635
|
* Прод недоступен / ответ без поручений → пустые списки (демон работает дальше).
|
|
7566
7636
|
*/
|
|
7567
|
-
async heartbeat(daemonVersion, pause, capacity, resources, storm) {
|
|
7637
|
+
async heartbeat(daemonVersion, pause, capacity, resources, storm, signIn) {
|
|
7568
7638
|
try {
|
|
7569
7639
|
const args = this.repoFullNames.length ? { repoFullNames: this.repoFullNames } : {};
|
|
7570
7640
|
if (daemonVersion) args.daemonVersion = daemonVersion;
|
|
@@ -7574,6 +7644,10 @@ var SwarmClient = class _SwarmClient {
|
|
|
7574
7644
|
args.paused = !!pause?.paused;
|
|
7575
7645
|
if (pause?.reason) args.pauseReason = pause.reason;
|
|
7576
7646
|
}
|
|
7647
|
+
if (signIn) {
|
|
7648
|
+
args.signedIn = signIn.signedIn;
|
|
7649
|
+
if (signIn.webUrl) args.webUrl = signIn.webUrl;
|
|
7650
|
+
}
|
|
7577
7651
|
if (storm !== void 0 && storm !== null) {
|
|
7578
7652
|
args.storm = storm.active;
|
|
7579
7653
|
if (storm.reason) args.stormReason = storm.reason;
|
|
@@ -7805,6 +7879,8 @@ var SwarmClient = class _SwarmClient {
|
|
|
7805
7879
|
// существующая локальная запись НЕ перетирается (поле не в field-copy ниже),
|
|
7806
7880
|
// в отличие от respawns, — авто-ревью v2, TASWM-162.
|
|
7807
7881
|
review_cycle: 0,
|
|
7882
|
+
// TASWM-451: модель ПРОВЕРКИ — прод-поле, заполняется ниже из ответа (как base_branch).
|
|
7883
|
+
review_model: null,
|
|
7808
7884
|
task_title: null,
|
|
7809
7885
|
task_desc: null,
|
|
7810
7886
|
task_url: null,
|
|
@@ -7857,6 +7933,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
7857
7933
|
local.area = pr.area ?? null;
|
|
7858
7934
|
local.depends_on = Array.isArray(pr.dependsOn) ? pr.dependsOn : [];
|
|
7859
7935
|
local.model = pr.model ?? null;
|
|
7936
|
+
local.review_model = pr.reviewModel ?? null;
|
|
7860
7937
|
local.effort = toEffortLevel(pr.effort);
|
|
7861
7938
|
local.cost_usd = typeof pr.costUsd === "number" ? pr.costUsd : 0;
|
|
7862
7939
|
local.tokens_in = typeof pr.tokensIn === "number" ? pr.tokensIn : 0;
|
|
@@ -8895,12 +8972,26 @@ function pick(v) {
|
|
|
8895
8972
|
const s = typeof v === "string" ? v.trim() : "";
|
|
8896
8973
|
return s.length > 0 ? s : null;
|
|
8897
8974
|
}
|
|
8975
|
+
function isReviewKind(kind) {
|
|
8976
|
+
return kind === "reviewer" || kind === "verifier";
|
|
8977
|
+
}
|
|
8978
|
+
function modelRoleOf(kind) {
|
|
8979
|
+
return isReviewKind(kind) ? "review" : "executor";
|
|
8980
|
+
}
|
|
8898
8981
|
function forKind(kind, swarm, norm) {
|
|
8899
8982
|
if (!swarm) return null;
|
|
8900
|
-
return kind
|
|
8983
|
+
return isReviewKind(kind) ? norm(swarm.reviewer) ?? norm(swarm.drone) : norm(swarm.drone);
|
|
8984
|
+
}
|
|
8985
|
+
function resolveRunModelRef(run, swarm, env = process.env) {
|
|
8986
|
+
const configured = parseModelRef(
|
|
8987
|
+
pick(run.model) ?? forKind(run.kind, swarm, pick) ?? pick(env.SWARM_DRONE_MODEL)
|
|
8988
|
+
);
|
|
8989
|
+
if (configured) return configured;
|
|
8990
|
+
const providerId = pick(env.SWARM_MODEL_PROVIDER) ?? DEFAULT_PROVIDER_ID;
|
|
8991
|
+
return { providerId, model: defaultModelFor(modelRoleOf(run.kind), providerId) };
|
|
8901
8992
|
}
|
|
8902
8993
|
function resolveRunModel(run, swarm, env = process.env) {
|
|
8903
|
-
return
|
|
8994
|
+
return resolveRunModelRef(run, swarm, env).model;
|
|
8904
8995
|
}
|
|
8905
8996
|
function resolveRunEffort(run, swarm, env = process.env) {
|
|
8906
8997
|
return toEffortLevel(run.effort) ?? forKind(run.kind, swarm, toEffortLevel) ?? toEffortLevel(env.SWARM_DRONE_EFFORT) ?? null;
|
|
@@ -10122,11 +10213,16 @@ import { z } from "zod";
|
|
|
10122
10213
|
init_src();
|
|
10123
10214
|
|
|
10124
10215
|
// src/reviewCycle.ts
|
|
10216
|
+
init_src();
|
|
10125
10217
|
function reviewTransitionPatch(run, prUrl) {
|
|
10126
|
-
const isFixIteration = run.review_verdict === "changes";
|
|
10218
|
+
const isFixIteration = run.review_verdict === "changes" || isNegativeVerifyVerdict(toVerifyVerdict(run.verify_verdict));
|
|
10127
10219
|
if (isFixIteration) {
|
|
10128
10220
|
const cycle = (run.review_cycle ?? 0) + 1;
|
|
10129
|
-
return {
|
|
10221
|
+
return {
|
|
10222
|
+
isFixIteration: true,
|
|
10223
|
+
cycle,
|
|
10224
|
+
patch: { status: "review", pr_url: prUrl, review_cycle: cycle, review_verdict: null, verify_verdict: null }
|
|
10225
|
+
};
|
|
10130
10226
|
}
|
|
10131
10227
|
return { isFixIteration: false, cycle: null, patch: { status: "review", pr_url: prUrl } };
|
|
10132
10228
|
}
|
|
@@ -12201,7 +12297,7 @@ async function changedPaths(worktree, opts = {}) {
|
|
|
12201
12297
|
// package.json
|
|
12202
12298
|
var package_default = {
|
|
12203
12299
|
name: "@taskless-app/swarm",
|
|
12204
|
-
version: "0.6.
|
|
12300
|
+
version: "0.6.14",
|
|
12205
12301
|
type: "module",
|
|
12206
12302
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
12207
12303
|
keywords: [
|
|
@@ -12251,7 +12347,7 @@ var package_default = {
|
|
|
12251
12347
|
execa: "^10.0.0",
|
|
12252
12348
|
zod: "3.25.76"
|
|
12253
12349
|
},
|
|
12254
|
-
gitHead: "
|
|
12350
|
+
gitHead: "a3b11498dbed5db3642fe4fc83bcd2054efde055"
|
|
12255
12351
|
};
|
|
12256
12352
|
|
|
12257
12353
|
// src/version.ts
|
|
@@ -15212,6 +15308,7 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
|
|
|
15212
15308
|
"3. `/verify` \u2014 \u0422\u041E\u041B\u042C\u041A\u041E \u0435\u0441\u043B\u0438 \u043C\u0435\u043D\u044F\u043B \u0432\u0438\u0434\u0438\u043C\u044B\u0439 UI/\u0432\u0438\u0437\u0443\u0430\u043B: \u043F\u0440\u043E\u0433\u043E\u043D\u0438 \u0444\u0438\u0447\u0443 \u043D\u0430 \u0436\u0438\u0432\u043E\u043C \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438 (\u043A\u0430\u043A \u0435\u0433\u043E \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u2014 \u0432 \u0441\u043A\u0438\u043B\u043B\u0430\u0445 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F) \u0438 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438 \u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u0435, \u043D\u0435 \u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E \u043A\u043E\u043C\u043F\u0438\u043B\u0438\u0440\u0443\u0435\u0442\u0441\u044F.",
|
|
15213
15309
|
`4. \u0421\u043A\u0440\u0438\u043D\u044B mobile+desktop \u2014 \u0422\u041E\u041B\u042C\u041A\u041E \u0435\u0441\u043B\u0438 \u043C\u0435\u043D\u044F\u043B \u0432\u0438\u0434\u0438\u043C\u044B\u0439 UI/\u0432\u0438\u0437\u0443\u0430\u043B, \u041E\u0414\u041D\u041E\u0419 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439: \`swarm shot <url> <out-prefix>\` (headless Chromium \u0441\u0430\u043C \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u043E\u0431\u0430 \u0432\u044C\u044E\u043F\u043E\u0440\u0442\u0430, \u0441\u043D\u0438\u043C\u0430\u0435\u0442 \u0438 \u0432\u044B\u0445\u043E\u0434\u0438\u0442 \u2014 \u043D\u0435 \u0434\u0435\u0440\u0436\u0438\u0442 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u043C). \u041F\u043E\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C \u2014 \u043E\u0431\u0430 \u0444\u0430\u0439\u043B\u0430 \u0418\u0414\u0423\u0422 \u0412 \u0417\u0410\u0414\u0410\u0427\u0423: \u043F\u0435\u0440\u0435\u0434\u0430\u0439 \u0438\u0445 \u043F\u0443\u0442\u0438 \u0432 \`done(pr_url, summary, screenshots: ["\u2026-desktop.png", "\u2026-mobile.png"])\`, \u0434\u0435\u043C\u043E\u043D \u043F\u0440\u0438\u043B\u043E\u0436\u0438\u0442 \u0438\u0445 \u0432\u043B\u043E\u0436\u0435\u043D\u0438\u044F\u043C\u0438 \u043A \u0442\u0432\u043E\u0435\u043C\u0443 \u043E\u0442\u0447\u0451\u0442\u0443 (TASWM-294). \u0427\u0435\u043B\u043E\u0432\u0435\u043A \u0441\u043C\u043E\u0442\u0440\u0438\u0442 \u0442\u0440\u0435\u043A\u0435\u0440, \u0430 \u043D\u0435 \u0433\u0438\u0442\u0445\u0430\u0431: \u0437\u0430\u0434\u0430\u0447\u0430 \u0431\u0435\u0437 \u0441\u043A\u0440\u0438\u043D\u043E\u0432 = \u043D\u0435\u0434\u043E\u043A\u0430\u0437\u0430\u043D\u043D\u0430\u044F \u0440\u0430\u0431\u043E\u0442\u0430. \u0412 PR \u2014 \u0414\u041E\u041F\u041E\u041B\u041D\u0418\u0422\u0415\u041B\u042C\u041D\u041E, \u043F\u043E \u0436\u0435\u043B\u0430\u043D\u0438\u044E; \u0432\u043C\u0435\u0441\u0442\u043E \u0437\u0430\u0434\u0430\u0447\u0438 \u2014 \u043D\u0435\u0442. \u23F1 \u0425\u0430\u0440\u0434-\u043B\u0438\u043C\u0438\u0442 \u043D\u0430 \u0432\u0435\u0441\u044C \u0448\u0430\u0433 \u0441\u043A\u0440\u0438\u043D\u043E\u0432 \u2014 5 \u043C\u0438\u043D\u0443\u0442. \u041D\u0415 \u0433\u043E\u043D\u044F\u0439 Playwright/\u0441\u0438\u0441\u0442\u0435\u043C\u043D\u044B\u0439 Chrome \u0432\u0440\u0443\u0447\u043D\u0443\u044E \u043F\u043E \u0448\u0430\u0433\u0430\u043C (\u043E\u0442\u043A\u0440\u044B\u043B\u2192\u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0435\u043B\u2192\u043A\u043B\u0438\u043A\u043D\u0443\u043B\u2192\u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0435\u043B\u2026) \u2014 \u0442\u0430\u043A \u0434\u0440\u043E\u043D \u0414\u0412\u0410\u0416\u0414\u042B \u0437\u0430\u043B\u0438\u043F\u0430\u043B \u043D\u0430 \u044D\u0442\u043E\u043C (TASWM-30, >30 \u043C\u0438\u043D stall \u0431\u0435\u0437 \u0435\u0434\u0438\u043D\u043E\u0433\u043E \u043A\u043E\u043C\u043C\u0438\u0442\u0430 \u0434\u0430\u043B\u044C\u0448\u0435) \u0432\u043C\u0435\u0441\u0442\u043E \u0442\u043E\u0433\u043E \u0447\u0442\u043E\u0431\u044B \u0441\u0434\u0430\u0442\u044C \u0433\u043E\u0442\u043E\u0432\u0443\u044E \u0440\u0430\u0431\u043E\u0442\u0443. \u041D\u0435 \u0443\u043B\u043E\u0436\u0438\u043B\u0441\u044F \u0432 \u043B\u0438\u043C\u0438\u0442 \u0438\u043B\u0438 \u043A\u043E\u043C\u0430\u043D\u0434\u0430 \u043D\u0435 \u0438\u0434\u0451\u0442 \u2014 \u043F\u0440\u0438\u043B\u043E\u0436\u0438 \u0447\u0442\u043E \u0443\u0436\u0435 \u0441\u043D\u044F\u043B, \u0438\u043B\u0438 \u043F\u0440\u044F\u043C\u043E \u043D\u0430\u043F\u0438\u0448\u0438 \u0432 PR/\u0437\u0430\u0434\u0430\u0447\u0435 \xAB\u0441\u043A\u0440\u0438\u043D\u044B \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\xBB \u0438 \u0421\u0420\u0410\u0417\u0423 \u0432\u044B\u0437\u043E\u0432\u0438 done(pr_url) \u2014 \u043D\u0435 \u043F\u0435\u0440\u0435\u0438\u0433\u0440\u044B\u0432\u0430\u0439 \u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u043F\u043E \u043A\u0440\u0443\u0433\u0443.`,
|
|
15214
15310
|
"4a. \u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u0417\u0410 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0431\u0435\u0437 \u0433\u043E\u0442\u043E\u0432\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u0434\u0430\u0451\u0442 \u044D\u043A\u0440\u0430\u043D \u043B\u043E\u0433\u0438\u043D\u0430, \u0430 \u043D\u0435 \u0446\u0435\u043B\u0435\u0432\u043E\u0439 \u044D\u043A\u0440\u0430\u043D \u2014 \u0441\u043D\u0438\u043C\u0430\u0442\u044C \u0435\u0451 \u043D\u0443\u0436\u043D\u043E \u0441 `--storage-state <\u0444\u0430\u0439\u043B>` (\u043A\u0430\u043A \u0437\u0430\u0432\u0435\u0441\u0442\u0438 \u0442\u0430\u043A\u043E\u0439 \u0444\u0430\u0439\u043B \u0432 \u044D\u0442\u043E\u043C \u043F\u0440\u043E\u0435\u043A\u0442\u0435 \u2014 \u0432 \u0435\u0433\u043E \u0441\u043A\u0438\u043B\u043B\u0430\u0445). \u041F\u043E\u0440\u044F\u0434\u043E\u043A \u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u043E\u0432 \u0432\u0430\u0436\u0435\u043D: `--storage-state` \u0411\u0415\u0417 \u043F\u0440\u0435\u0434\u0448\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0435\u0433\u043E `<out-prefix>` \u043C\u043E\u043B\u0447\u0430 \u0441\u044A\u0435\u0434\u0430\u0435\u0442 \u0435\u0433\u043E \u043C\u0435\u0441\u0442\u043E, \u0438 \u0444\u043B\u0430\u0433 \u043D\u0438\u043A\u043E\u0433\u0434\u0430 \u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u0441\u044F (\u0441\u043C. shotCli.ts parseShotArgs) \u2014 \u0435\u0441\u043B\u0438 \u043D\u0430 \u0441\u043D\u0438\u043C\u043A\u0435 \u044D\u043A\u0440\u0430\u043D \u043B\u043E\u0433\u0438\u043D\u0430 \u0432\u043C\u0435\u0441\u0442\u043E \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F, \u043F\u0440\u043E\u0432\u0435\u0440\u044C \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u044D\u0442\u043E.",
|
|
15311
|
+
"4b. \u0421\u041D\u0418\u041C\u0410\u0419 \u041F\u0420\u041E\u0414\u0423\u041A\u0422, \u0410 \u041D\u0415 \u041F\u0420\u0415\u0412\u042C\u042E-\u0421\u0422\u0420\u0410\u041D\u0418\u0426\u0423 (TASWM-404). \u0414\u0435\u043C\u043E-\u0440\u043E\u0443\u0442 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430 (`/dev/*`, \u043F\u0435\u0441\u043E\u0447\u043D\u0438\u0446\u0430, \u0441\u0442\u043E\u0440\u0438 \u0432 \u043C\u043E\u0431\u0438\u043B\u044C\u043D\u043E\u043C \u0444\u0440\u0435\u0439\u043C\u0435) \u2014 \u041D\u0415 \u0434\u043E\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u044C\u0441\u0442\u0432\u043E \u043C\u043E\u0431\u0438\u043B\u044C\u043D\u043E\u0439 \u0432\u0451\u0440\u0441\u0442\u043A\u0438: \u0443 \u0442\u0430\u043A\u043E\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B \u0441\u0432\u043E\u044F \u043E\u0431\u043E\u043B\u043E\u0447\u043A\u0430, \u0435\u0451 \u043C\u0435\u043D\u044E \u043E\u0431\u044B\u0447\u043D\u043E \u043D\u0435 \u0441\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u043D\u0430 \u0443\u0437\u043A\u043E\u043C \u044D\u043A\u0440\u0430\u043D\u0435, \u0438 \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u043C\u0443 \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u043F\u043E\u043B\u043E\u0441\u0430 \u0432 \u043F\u0430\u0440\u0443 \u0441\u043E\u0442\u0435\u043D \u043F\u0438\u043A\u0441\u0435\u043B\u0435\u0439 \u0432\u043C\u0435\u0441\u0442\u043E \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u0439 \u0448\u0438\u0440\u0438\u043D\u044B. \u041F\u043E \u0442\u0430\u043A\u043E\u043C\u0443 \u0441\u043D\u0438\u043C\u043A\u0443 \u043D\u0435 \u0432\u0438\u0434\u043D\u043E \u043D\u0438 \u0448\u0438\u0440\u0438\u043D\u044B \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432, \u043D\u0438 \u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u044F \u0448\u0430\u043F\u043A\u0438, \u043D\u0438 \u043D\u0430\u043F\u043E\u043B\u0437\u0430\u043D\u0438\u044F \u043B\u0438\u043F\u043A\u0438\u0445 \u043F\u043E\u0434\u043F\u0438\u0441\u0435\u0439 \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u0440\u043E\u0445\u043E\u0434\u0438\u0442 \u0412\u0421\u0415\u0413\u0414\u0410 \u0438 \u043D\u0435 \u043B\u043E\u0432\u0438\u0442 \u043D\u0438\u0447\u0435\u0433\u043E (\u0442\u0430\u043A \u0441\u0434\u0430\u043B\u0438 \u0441\u043B\u043E\u043C\u0430\u043D\u043D\u0443\u044E \u043C\u043E\u0431\u0438\u043B\u0443 \u0432 TASWM-403, \u0441\u043A\u0440\u0438\u043D \u043F\u0440\u0438 \u044D\u0442\u043E\u043C \u0431\u044B\u043B \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D). \u0421\u043D\u0438\u043C\u0430\u0439 \u0420\u0415\u0410\u041B\u042C\u041D\u042B\u0419 \u0440\u043E\u0443\u0442 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u043F\u043E\u0434 \u0436\u0438\u0432\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0435\u0439; \u043A\u0430\u043A \u044D\u0442\u043E \u0441\u0434\u0435\u043B\u0430\u0442\u044C \u043E\u0434\u043D\u043E\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 \u0432 \u044D\u0442\u043E\u043C \u043F\u0440\u043E\u0435\u043A\u0442\u0435 \u2014 \u0432 \u0435\u0433\u043E \u0441\u043A\u0438\u043B\u043B\u0430\u0445. \u041F\u0440\u0435\u0432\u044C\u044E \u0437\u0430\u043A\u043E\u043D\u043D\u043E \u0440\u043E\u0432\u043D\u043E \u0442\u043E\u0433\u0434\u0430, \u043A\u043E\u0433\u0434\u0430 \u043F\u0440\u0430\u0432\u0438\u0448\u044C \u0441\u0430\u043C \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442 \u0432\u043D\u0435 \u043E\u0431\u043E\u043B\u043E\u0447\u043A\u0438 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F.",
|
|
15215
15312
|
"\u0427\u0438\u0441\u0442\u043E \u0431\u044D\u043A\u0435\u043D\u0434/\u043B\u043E\u0433\u0438\u043A\u0443/API/\u0434\u0435\u043C\u043E\u043D/CI/\u0442\u0438\u043F\u044B \u2014 \u0448\u0430\u0433\u0438 3\u20134 \u043F\u0440\u043E\u043F\u0443\u0441\u0442\u0438 (\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u041D\u0415 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0439 \u0438 \u0441\u043A\u0440\u0438\u043D\u044B \u043D\u0435 \u0441\u043D\u0438\u043C\u0430\u0439): \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0435 \u0442\u0435\u0441\u0442\u044B (\u0448\u0430\u0433 1) + e2e \u0432 CI \u044D\u0442\u043E \u0443\u0436\u0435 \u043F\u043E\u043A\u0440\u044B\u0432\u0430\u044E\u0442; \u044D\u043A\u043E\u043D\u043E\u043C\u0438\u043C \u0432\u0440\u0435\u043C\u044F \u0438 \u0442\u043E\u043A\u0435\u043D\u044B.",
|
|
15216
15313
|
"\u0422\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \u044D\u0442\u043E\u0433\u043E \u0437\u043E\u0432\u0438 done(pr_url), \u0438 \u0432 \u0441\u0434\u0430\u0447\u0435 \u043F\u043E\u043A\u0430\u0436\u0438 \u0434\u043E\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u044C\u0441\u0442\u0432\u043E (\u0437\u0435\u043B\u0451\u043D\u044B\u0439 \u0432\u044B\u0432\u043E\u0434/\u043D\u0430\u0431\u043B\u044E\u0434\u0430\u0435\u043C\u043E\u0435 \u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u0435), \u0430 \u043D\u0435 \xAB\u0434\u043E\u043B\u0436\u043D\u043E \u0440\u0430\u0431\u043E\u0442\u0430\u0442\u044C\xBB.",
|
|
15217
15314
|
"\u0421\u043A\u0438\u043B\u043B\u044B \u043F\u0440\u0438\u0445\u043E\u0434\u044F\u0442 \u043A \u0442\u0435\u0431\u0435 \u0414\u0412\u0423\u041C\u042F \u043D\u0430\u0431\u043E\u0440\u0430\u043C\u0438, \u0438 \u043E\u0431\u0430 \u0443\u0436\u0435 \u043F\u043E\u0434\u0445\u0432\u0430\u0447\u0435\u043D\u044B (\u0441\u043F\u0430\u0432\u043D \u0438\u0437\u043E\u043B\u0438\u0440\u043E\u0432\u0430\u043D \u043E\u0442 \u044E\u0437\u0435\u0440\u0441\u043A\u043E\u0433\u043E ~/.claude, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u041B\u0418\u0427\u041D\u042B\u0415 \u0441\u043A\u0438\u043B\u043B\u044B \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u0442\u0435\u0431\u0435 \u043D\u0435 \u0432\u0438\u0434\u043D\u044B). \u2460 \u0420\u043E\u0439\u043D\u044B\u0435 `swarm-drone:*` \u0435\u0434\u0443\u0442 \u0441 \u0420\u041E\u0415\u041C \u0432 \u043B\u044E\u0431\u043E\u0439 \u043F\u0440\u043E\u0435\u043A\u0442 \u2014 \u0434\u0438\u0441\u0446\u0438\u043F\u043B\u0438\u043D\u0430, \u0432\u0435\u0440\u043D\u0430\u044F \u0432\u0435\u0437\u0434\u0435: swarm-save-often (\u043D\u0435\u0437\u0430\u043F\u0443\u0448\u0435\u043D\u043D\u043E\u0435 \u0443\u043C\u0438\u0440\u0430\u0435\u0442 \u0441 \u0434\u0440\u043E\u043D\u043E\u043C), swarm-merge-main (\u0431\u0430\u0437\u043E\u0432\u0443\u044E \u0432\u0435\u0442\u043A\u0443 \u0432\u043B\u0438\u0432\u0430\u0442\u044C, \u0430 \u043D\u0435 \u0440\u0435\u0431\u0435\u0439\u0437\u0438\u0442\u044C \u0437\u0430\u043F\u0443\u0448\u0435\u043D\u043D\u0443\u044E), swarm-await-process (\u0436\u0434\u0430\u0442\u044C \u0443\u0441\u043B\u043E\u0432\u0438\u0435, \u043D\u0435 sleep), swarm-verify-green (\u0447\u0442\u043E \u0441\u0447\u0438\u0442\u0430\u0442\u044C \u0437\u0435\u043B\u0451\u043D\u044B\u043C), systematic-debugging (\u043F\u0435\u0440\u0432\u043E\u043F\u0440\u0438\u0447\u0438\u043D\u0430 \u0434\u043E \u0444\u0438\u043A\u0441\u0430). \u2461 \u0421\u043A\u0438\u043B\u043B\u044B \u0420\u0415\u041F\u041E\u0417\u0418\u0422\u041E\u0420\u0418\u042F (.claude/skills, \u0441\u043E\u0441\u0442\u0430\u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043E\u0442 \u043F\u0440\u043E\u0435\u043A\u0442\u0430) \u2014 \u0437\u043D\u0430\u043D\u0438\u044F \u043E\u0431 \u044D\u0442\u043E\u043C \u043F\u0440\u043E\u0435\u043A\u0442\u0435: \u043A\u0430\u043A \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u0441\u0442\u0435\u043A, \u043A\u0430\u043A \u0433\u043E\u043D\u044F\u0442\u044C \u0442\u0435\u0441\u0442\u044B \u0438 \u0447\u0442\u043E \u0437\u0434\u0435\u0441\u044C \u0437\u0435\u043B\u0451\u043D\u043E\u0435, UI-\u043F\u0440\u0430\u0432\u0438\u043B\u0430. \u041F\u043E\u043B\u044C\u0437\u0443\u0439\u0441\u044F \u043E\u0431\u043E\u0438\u043C\u0438, \u043D\u0435 \u043F\u0435\u0440\u0435\u0438\u0437\u043E\u0431\u0440\u0435\u0442\u0430\u0439; \u043D\u0435\u0442 \u043D\u0443\u0436\u043D\u043E\u0433\u043E \u0437\u043D\u0430\u043D\u0438\u044F \u043E \u043F\u0440\u043E\u0435\u043A\u0442\u0435 \u2014 \u044D\u0442\u043E \u043F\u0440\u043E\u0431\u0435\u043B \u0435\u0433\u043E \u0441\u043A\u0438\u043B\u043B\u043E\u0432, \u0441\u043A\u0430\u0436\u0438 note(), \u0430 \u043D\u0435 \u0432\u044B\u0434\u0443\u043C\u044B\u0432\u0430\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u044B."
|
|
@@ -16804,7 +16901,7 @@ async function tickOnce(deps) {
|
|
|
16804
16901
|
} else if (!st.conflicting && deps.ci && deps.respawn && !stormActive) {
|
|
16805
16902
|
await handleCi(deps, run, now, worktreeExists, hasTranscript, droneCap);
|
|
16806
16903
|
}
|
|
16807
|
-
if (run.review_verdict === "changes" && !stormActive && !st.conflicting && !deps.db.activeResolverFor(run.id) && await wakeAuthorOnChanges(deps, run, now, droneCap)) {
|
|
16904
|
+
if ((run.review_verdict === "changes" || isNegativeVerifyVerdict(toVerifyVerdict(run.verify_verdict))) && !stormActive && !st.conflicting && !deps.db.activeResolverFor(run.id) && await wakeAuthorOnChanges(deps, run, now, droneCap)) {
|
|
16808
16905
|
continue;
|
|
16809
16906
|
}
|
|
16810
16907
|
const reviewerGatesOk = !st.conflicting && !st.isDraft && !!deps.spawnReviewer && !stormActive && (!deps.reviewCutoffAt || run.updated_at >= deps.reviewCutoffAt) && deps.db.reviewerCountFor(run.id) === 0 && // ГЕЙТ ПЕРВЫМ: живой/успешный ревьюер уже есть — ни капа, ни спавна не трогаем
|
|
@@ -17227,8 +17324,14 @@ async function replayDecision(deps, qid, chosen, by = "human") {
|
|
|
17227
17324
|
repo: old.repo,
|
|
17228
17325
|
area: old.area,
|
|
17229
17326
|
decision: { question: q.text, options: q.options, chosen, prior: q.answer, priorState: old.status },
|
|
17230
|
-
workspace_id: old.workspace_id
|
|
17327
|
+
workspace_id: old.workspace_id,
|
|
17231
17328
|
// переигровка той же задачи — тот же воркспейс (TASWM-288)
|
|
17329
|
+
// TASWM-451: та же ЗАДАЧА — те же модели. Переигровка это продолжение той же работы, а не
|
|
17330
|
+
// новая: без переноса любой вопрос дрона оператору молча возвращал бы задачу на дефолт
|
|
17331
|
+
// роли, и выбор постановщика («эту делаем сильной моделью») терялся бы ровно там, где
|
|
17332
|
+
// работа оказалась сложной. Тот же довод, что у area и workspace_id строкой выше.
|
|
17333
|
+
model: old.model,
|
|
17334
|
+
review_model: old.review_model
|
|
17232
17335
|
});
|
|
17233
17336
|
deps.db.updateRun(old.id, { status: "superseded" });
|
|
17234
17337
|
deps.db.supersede(old.id, fresh.id, by);
|
|
@@ -17256,8 +17359,13 @@ async function resolveConflict(deps, reviewRun) {
|
|
|
17256
17359
|
area: reviewRun.area,
|
|
17257
17360
|
// держим area-lock, пока чиним — не начинаем конфликтующее в зоне
|
|
17258
17361
|
resolves_run_id: reviewRun.id,
|
|
17259
|
-
workspace_id: reviewRun.workspace_id
|
|
17362
|
+
workspace_id: reviewRun.workspace_id,
|
|
17260
17363
|
// та же задача, тот же воркспейс (TASWM-288)
|
|
17364
|
+
// TASWM-451: резолвер доделывает ТУ ЖЕ работу — разводит конфликт в её же ветке, и
|
|
17365
|
+
// модель ему нужна та же, что выбрали задаче. Модель проверки несём дальше, чтобы её не
|
|
17366
|
+
// потеряли проверяющие, которых поднимут уже по прогону резолвера.
|
|
17367
|
+
model: reviewRun.model,
|
|
17368
|
+
review_model: reviewRun.review_model
|
|
17261
17369
|
});
|
|
17262
17370
|
const wt = join29(deps.worktreesDir, basename4(repo), `${reviewRun.task_slug}-resolve-${resolver.id}`);
|
|
17263
17371
|
try {
|
|
@@ -17304,6 +17412,11 @@ async function spawnReviewer(deps, reviewRun, files) {
|
|
|
17304
17412
|
task_slug: reviewRun.task_slug,
|
|
17305
17413
|
repo,
|
|
17306
17414
|
kind: "reviewer",
|
|
17415
|
+
// TASWM-451: ЧЕМ проверять — решение постановщика, приехавшее с задачей на прогон
|
|
17416
|
+
// исполнителя. Пин, а не «спросим настройки на спавне»: ревьюер заводится локально, и
|
|
17417
|
+
// задача ему недоступна. Пусто в задаче → null → дефолт роли (модель проверки).
|
|
17418
|
+
model: reviewRun.review_model,
|
|
17419
|
+
review_model: reviewRun.review_model,
|
|
17307
17420
|
reviews_run_id: reviewRun.id,
|
|
17308
17421
|
review_cycle: reviewRun.review_cycle,
|
|
17309
17422
|
// штамп цикла (авто-ревью v2, TASWM-162): reviewerCountFor текущего цикла
|
|
@@ -17376,6 +17489,11 @@ async function spawnVerifier(deps, verifiedRun, files) {
|
|
|
17376
17489
|
task_slug: verifiedRun.task_slug,
|
|
17377
17490
|
repo,
|
|
17378
17491
|
kind: "verifier",
|
|
17492
|
+
// TASWM-451: та же модель проверки, что у ревьюера, и из того же источника — задачи.
|
|
17493
|
+
// Две проверки одного PR должны стоить одинаково, иначе одна из них незаметно уедет
|
|
17494
|
+
// на модель исполнителя (ровно это и стоило $45.94 на TAS-524).
|
|
17495
|
+
model: verifiedRun.review_model,
|
|
17496
|
+
review_model: verifiedRun.review_model,
|
|
17379
17497
|
verifies_run_id: verifiedRun.id,
|
|
17380
17498
|
review_cycle: verifiedRun.review_cycle,
|
|
17381
17499
|
// штамп цикла: фикс-итерацию проверяет свежий верификатор
|
|
@@ -17746,7 +17864,17 @@ async function runDaemon(opts) {
|
|
|
17746
17864
|
control.state ?? null,
|
|
17747
17865
|
lastOwnCapacity,
|
|
17748
17866
|
hostResourcesForProd(),
|
|
17749
|
-
{ active: stormActive, reason: stormStartedAt ? `\u043D\u043E\u0432\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u0441 ${stormStartedAt} UTC` : null }
|
|
17867
|
+
{ active: stormActive, reason: stormStartedAt ? `\u043D\u043E\u0432\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u0441 ${stormStartedAt} UTC` : null },
|
|
17868
|
+
// TASWM-393: ВХОД в Taskless — та же природа, что пауза и шторм: состояние
|
|
17869
|
+
// машины, о котором знает только она. Пульс идёт и без входа, поэтому «на
|
|
17870
|
+
// связи» без этого поля читалось как «возьмёт работу» (12.08 — шесть прогонов
|
|
17871
|
+
// в очереди у машины, которая снаружи выглядела здоровой). Адрес шлём
|
|
17872
|
+
// ФАКТИЧЕСКИЙ (web.port), а не зашитый: порт настраивается.
|
|
17873
|
+
// signedInToTaskless(), а НЕ !authStateLine(): отказ — это «токен есть, но его
|
|
17874
|
+
// отвергают», а 12.08 входа не делали вовсе, и по отказу машина числилась бы
|
|
17875
|
+
// вошедшей. Адрес — WEB_PORT (он же SWARM_WEB_PORT из окружения, на нём и
|
|
17876
|
+
// поднимается морда): зашитый 7420 отправил бы человека туда, где ничего нет.
|
|
17877
|
+
{ signedIn: signedInToTaskless(), webUrl: `http://127.0.0.1:${WEB_PORT}` }
|
|
17750
17878
|
);
|
|
17751
17879
|
if (reply.maxDrones !== void 0) operatorMaxDrones = reply.maxDrones;
|
|
17752
17880
|
if (reply.commands.length) onHostCommands(reply.commands);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taskless-app/swarm",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
6
6
|
"keywords": [
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"execa": "^10.0.0",
|
|
39
39
|
"zod": "3.25.76"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "a3b11498dbed5db3642fe4fc83bcd2054efde055",
|
|
42
42
|
"scripts": {
|
|
43
43
|
"overmind": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/overmind.ts",
|
|
44
44
|
"swarm": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/cli.ts",
|