@taskless-app/swarm 0.6.64 → 0.6.66

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 CHANGED
@@ -705,16 +705,17 @@ var init_modelPolicy = __esm({
705
705
  roles: { executor: "glm-5.3", review: "glm-5.3" }
706
706
  },
707
707
  // Grok CLI. Каталог — живой `grok models` (16.10.2026), целиком и без выдуманных
708
- // имён: grok-4.7 (дефолт CLI и исполнителя), grok-4.7-build-fast, grok-4.6, grok-4.5
709
- // TASWM-605: обе роли — на свежей модели (решение владельца: рой работает на свежих
710
- // моделях). Это только фолбэк: демон берёт дефолт живого `grok models` (liveModels.ts).
708
+ // имён: grok-4.7 (дефолт CLI и исполнителя), grok-4.7-build-fast, grok-4.6, grok-4.5.
709
+ // TASWM-607: исполнитель остаётся на свежей модели. Живой дефолт CLI демон применяет
710
+ // только к роли executor (liveModels.ts). Ревьюер и проверяющий — на облегчённой
711
+ // grok-4.7-build-fast, пока человек не задал свою модель ревьюеров.
711
712
  // Тарифа нет: цену пишет CLI.
712
713
  {
713
714
  id: "xai",
714
715
  label: "xAI (Grok)",
715
716
  executorId: "grok",
716
717
  selectable: true,
717
- roles: { executor: "grok-4.7", review: "grok-4.7" },
718
+ roles: { executor: "grok-4.7", review: "grok-4.7-build-fast" },
718
719
  models: ["grok-4.7", "grok-4.7-build-fast", "grok-4.6", "grok-4.5"]
719
720
  },
720
721
  // TASWM-565 · шаг 1 из четырёх: провайдер в реестре ЕСТЬ, выбрать его ещё нельзя.
@@ -1795,7 +1796,9 @@ var init_queuePlan = __esm({
1795
1796
  init_status();
1796
1797
  init_hostPool();
1797
1798
  init_hosts();
1799
+ init_modelPolicy();
1798
1800
  init_time();
1801
+ init_verifyVerdict();
1799
1802
  MIN_MS = 6e4;
1800
1803
  QUEUE_NOMINAL_RUN_MS = 25 * MIN_MS;
1801
1804
  QUEUE_MIN_TAIL_MS = 3 * MIN_MS;
@@ -2852,7 +2855,8 @@ var init_liveModels = __esm({
2852
2855
 
2853
2856
  // src/swarmModels.ts
2854
2857
  function roleDefault(role, providerId) {
2855
- return liveDefaultModel(providerId) ?? defaultModelFor(role, providerId);
2858
+ if (role === "executor") return liveDefaultModel(providerId) ?? defaultModelFor(role, providerId);
2859
+ return defaultModelFor(role, providerId);
2856
2860
  }
2857
2861
  function pick2(v) {
2858
2862
  const s = typeof v === "string" ? v.trim() : "";
@@ -3872,7 +3876,7 @@ var init_package = __esm({
3872
3876
  "package.json"() {
3873
3877
  package_default = {
3874
3878
  name: "@taskless-app/swarm",
3875
- version: "0.6.64",
3879
+ version: "0.6.66",
3876
3880
  type: "module",
3877
3881
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
3878
3882
  keywords: [
@@ -3923,7 +3927,7 @@ var init_package = __esm({
3923
3927
  execa: "^10.0.0",
3924
3928
  zod: "3.25.76"
3925
3929
  },
3926
- gitHead: "7f27042a00bf1554798cce7b8a233ee6c5686a50"
3930
+ gitHead: "a6bc0ea303cb5336fd4e130fba73d90365268b6f"
3927
3931
  };
3928
3932
  }
3929
3933
  });
@@ -9253,6 +9257,8 @@ var SwarmClient = class _SwarmClient {
9253
9257
  if (!(typeof n === "number" && Number.isFinite(n) && n > 0)) return null;
9254
9258
  const str = (v) => typeof v === "string" && v.trim() ? v.trim() : null;
9255
9259
  const cost = obj?.swarmSettings?.maxCostUsd;
9260
+ const rawLimit = obj?.swarmSettings?.reviewCycleLimit;
9261
+ const reviewCycleLimit = typeof rawLimit === "number" && Number.isInteger(rawLimit) && rawLimit >= 1 && rawLimit <= 20 ? rawLimit : null;
9256
9262
  return {
9257
9263
  maxDrones: Math.trunc(n),
9258
9264
  // Старый прод (до TASWM-364) поля swarmSettings не отдаёт вовсе — обе модели null,
@@ -9269,7 +9275,8 @@ var SwarmClient = class _SwarmClient {
9269
9275
  // значим («гарантия выключена осознанно»), поэтому пропускаем и его, а не только
9270
9276
  // положительные: null остаётся ровно за «ручка не задана / старый прод», и только
9271
9277
  // он уводит спавн на env-фолбэк и дефолт ядра.
9272
- maxCostUsd: typeof cost === "number" && Number.isFinite(cost) && cost >= 0 ? cost : null
9278
+ maxCostUsd: typeof cost === "number" && Number.isFinite(cost) && cost >= 0 ? cost : null,
9279
+ reviewCycleLimit
9273
9280
  };
9274
9281
  }
9275
9282
  /**
package/dist/overmind.js CHANGED
@@ -702,16 +702,17 @@ var init_modelPolicy = __esm({
702
702
  roles: { executor: "glm-5.3", review: "glm-5.3" }
703
703
  },
704
704
  // Grok CLI. Каталог — живой `grok models` (16.10.2026), целиком и без выдуманных
705
- // имён: grok-4.7 (дефолт CLI и исполнителя), grok-4.7-build-fast, grok-4.6, grok-4.5
706
- // TASWM-605: обе роли — на свежей модели (решение владельца: рой работает на свежих
707
- // моделях). Это только фолбэк: демон берёт дефолт живого `grok models` (liveModels.ts).
705
+ // имён: grok-4.7 (дефолт CLI и исполнителя), grok-4.7-build-fast, grok-4.6, grok-4.5.
706
+ // TASWM-607: исполнитель остаётся на свежей модели. Живой дефолт CLI демон применяет
707
+ // только к роли executor (liveModels.ts). Ревьюер и проверяющий — на облегчённой
708
+ // grok-4.7-build-fast, пока человек не задал свою модель ревьюеров.
708
709
  // Тарифа нет: цену пишет CLI.
709
710
  {
710
711
  id: "xai",
711
712
  label: "xAI (Grok)",
712
713
  executorId: "grok",
713
714
  selectable: true,
714
- roles: { executor: "grok-4.7", review: "grok-4.7" },
715
+ roles: { executor: "grok-4.7", review: "grok-4.7-build-fast" },
715
716
  models: ["grok-4.7", "grok-4.7-build-fast", "grok-4.6", "grok-4.5"]
716
717
  },
717
718
  // TASWM-565 · шаг 1 из четырёх: провайдер в реестре ЕСТЬ, выбрать его ещё нельзя.
@@ -2026,7 +2027,9 @@ var init_queuePlan = __esm({
2026
2027
  init_status();
2027
2028
  init_hostPool();
2028
2029
  init_hosts();
2030
+ init_modelPolicy();
2029
2031
  init_time();
2032
+ init_verifyVerdict();
2030
2033
  MIN_MS = 6e4;
2031
2034
  QUEUE_NOMINAL_RUN_MS = 25 * MIN_MS;
2032
2035
  QUEUE_MIN_TAIL_MS = 3 * MIN_MS;
@@ -3099,7 +3102,8 @@ var init_liveModels = __esm({
3099
3102
 
3100
3103
  // src/swarmModels.ts
3101
3104
  function roleDefault(role, providerId) {
3102
- return liveDefaultModel(providerId) ?? defaultModelFor(role, providerId);
3105
+ if (role === "executor") return liveDefaultModel(providerId) ?? defaultModelFor(role, providerId);
3106
+ return defaultModelFor(role, providerId);
3103
3107
  }
3104
3108
  function pick2(v) {
3105
3109
  const s = typeof v === "string" ? v.trim() : "";
@@ -3135,6 +3139,10 @@ function resolveRunModelRef(run2, swarm, env = process.env) {
3135
3139
  const providerId = pick2(env.SWARM_MODEL_PROVIDER) ?? DEFAULT_PROVIDER_ID;
3136
3140
  return { providerId, model: roleDefault(modelRoleOf(run2.kind), providerId) };
3137
3141
  }
3142
+ function modelRefToRecord(run2, ref) {
3143
+ if (!run2 || pick2(run2.model)) return null;
3144
+ return `${ref.providerId}/${ref.model}`;
3145
+ }
3138
3146
  function resolveRunEffort(run2, swarm, env = process.env) {
3139
3147
  return toEffortLevel(run2.effort) ?? forKind(run2.kind, swarm, toEffortLevel) ?? toEffortLevel(env.SWARM_DRONE_EFFORT) ?? null;
3140
3148
  }
@@ -9525,6 +9533,8 @@ var SwarmClient = class _SwarmClient {
9525
9533
  if (!(typeof n === "number" && Number.isFinite(n) && n > 0)) return null;
9526
9534
  const str2 = (v) => typeof v === "string" && v.trim() ? v.trim() : null;
9527
9535
  const cost = obj?.swarmSettings?.maxCostUsd;
9536
+ const rawLimit = obj?.swarmSettings?.reviewCycleLimit;
9537
+ const reviewCycleLimit = typeof rawLimit === "number" && Number.isInteger(rawLimit) && rawLimit >= 1 && rawLimit <= 20 ? rawLimit : null;
9528
9538
  return {
9529
9539
  maxDrones: Math.trunc(n),
9530
9540
  // Старый прод (до TASWM-364) поля swarmSettings не отдаёт вовсе — обе модели null,
@@ -9541,7 +9551,8 @@ var SwarmClient = class _SwarmClient {
9541
9551
  // значим («гарантия выключена осознанно»), поэтому пропускаем и его, а не только
9542
9552
  // положительные: null остаётся ровно за «ручка не задана / старый прод», и только
9543
9553
  // он уводит спавн на env-фолбэк и дефолт ядра.
9544
- maxCostUsd: typeof cost === "number" && Number.isFinite(cost) && cost >= 0 ? cost : null
9554
+ maxCostUsd: typeof cost === "number" && Number.isFinite(cost) && cost >= 0 ? cost : null,
9555
+ reviewCycleLimit
9545
9556
  };
9546
9557
  }
9547
9558
  /**
@@ -13056,7 +13067,7 @@ function parkReasonUnretriable(reason) {
13056
13067
  // package.json
13057
13068
  var package_default = {
13058
13069
  name: "@taskless-app/swarm",
13059
- version: "0.6.64",
13070
+ version: "0.6.66",
13060
13071
  type: "module",
13061
13072
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
13062
13073
  keywords: [
@@ -13107,7 +13118,7 @@ var package_default = {
13107
13118
  execa: "^10.0.0",
13108
13119
  zod: "3.25.76"
13109
13120
  },
13110
- gitHead: "7f27042a00bf1554798cce7b8a233ee6c5686a50"
13121
+ gitHead: "a6bc0ea303cb5336fd4e130fba73d90365268b6f"
13111
13122
  };
13112
13123
 
13113
13124
  // src/version.ts
@@ -19123,6 +19134,14 @@ var BASE_NOOP_MARK = "\u0432\u043B\u0438\u0432\u0430\u0442\u044C \u043D\u0435\u0
19123
19134
  var RESOLVER_RESOLVED_MARK = "\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0441 main \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D \u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440\u043E\u043C";
19124
19135
  var REVIEW_SPAWN_LIMIT = Number(process.env.SWARM_REVIEW_SPAWN_LIMIT) || 3;
19125
19136
  var REVIEW_CYCLE_LIMIT = Number(process.env.SWARM_REVIEW_CYCLE_LIMIT) || 3;
19137
+ var REVIEW_CYCLE_LIMIT_CAP = 20;
19138
+ var configuredReviewCycleLimit = null;
19139
+ function configureReviewCycleLimit(n) {
19140
+ configuredReviewCycleLimit = n != null && Number.isInteger(n) && n >= 1 && n <= REVIEW_CYCLE_LIMIT_CAP ? n : null;
19141
+ }
19142
+ function effectiveReviewCycleLimit() {
19143
+ return configuredReviewCycleLimit ?? REVIEW_CYCLE_LIMIT;
19144
+ }
19126
19145
  var CI_WAKE_LIMIT = Number(process.env.SWARM_CI_WAKE_LIMIT) || 3;
19127
19146
  var CI_WAKE_COOLDOWN_MS = Number(process.env.SWARM_CI_WAKE_COOLDOWN_MS) || 3 * 60 * 1e3;
19128
19147
  var CI_CHECK_COOLDOWN_MS = Number(process.env.SWARM_CI_CHECK_COOLDOWN_MS) || 60 * 1e3;
@@ -19605,11 +19624,13 @@ function relinkUnboundReviewers(deps) {
19605
19624
  narrate(deps, `${droneLabel(rv)} \u0440\u0430\u0431\u043E\u0442\u0430\u043B \u0431\u0435\u0437 \u043F\u0440\u0438\u0432\u044F\u0437\u043A\u0438 \u043A \u043F\u0440\u043E\u0433\u043E\u043D\u0443 \u2014 \u0441\u0432\u044F\u0437\u0430\u043B \u043F\u043E PR-\u0441\u0441\u044B\u043B\u043A\u0435, \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0442\u0435\u043F\u0435\u0440\u044C \u043B\u044F\u0436\u0435\u0442 \u0432 ${parent.task_slug}, \u0430 \u043D\u0435 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 \u043A\u043E\u043C\u043C\u0435\u043D\u0442 GitHub.`);
19606
19625
  }
19607
19626
  }
19608
- var ESCALATION_LIMITS = { reviewSpawn: REVIEW_SPAWN_LIMIT, reviewCycle: REVIEW_CYCLE_LIMIT };
19627
+ function escalationLimits() {
19628
+ return { reviewSpawn: REVIEW_SPAWN_LIMIT, reviewCycle: effectiveReviewCycleLimit() };
19629
+ }
19609
19630
  function deliberateReviewStop(db, run2) {
19610
19631
  const texts = db.events(run2.id).map((e) => e.text ?? "");
19611
19632
  const hit = findEscalation(texts, run2, "reviewer");
19612
- return hit?.stopReason?.(run2, ESCALATION_LIMITS) ?? null;
19633
+ return hit?.stopReason?.(run2, escalationLimits()) ?? null;
19613
19634
  }
19614
19635
  function noteReviewBlocked(deps, run2, reason) {
19615
19636
  if (!shouldNoteReason(deps.db, run2.id, reason)) return;
@@ -20591,7 +20612,7 @@ async function spawnFreshReviewerAfterStall(deps, run2, why) {
20591
20612
  deps.db.addEvent(run2.id, "step", `\u0441\u0432\u0435\u0436\u0438\u0439 \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u043D\u0435 \u043D\u0443\u0436\u0435\u043D: ${live2[0].name} \u0435\u0449\u0451 \u0436\u0438\u0432 (${live2[0].stop_reason ?? live2[0].status})`);
20592
20613
  return;
20593
20614
  }
20594
- if (run2.review_cycle >= REVIEW_CYCLE_LIMIT || deps.db.reviewerAttemptCountFor(run2.id) >= REVIEW_SPAWN_LIMIT) {
20615
+ if (run2.review_cycle >= effectiveReviewCycleLimit() || deps.db.reviewerAttemptCountFor(run2.id) >= REVIEW_SPAWN_LIMIT) {
20595
20616
  return;
20596
20617
  }
20597
20618
  const gate = await prGateForReviewer(deps, run2, run2.pr_url, "\u043F\u043E\u043A\u0430 \u0436\u0434\u0430\u043B CI");
@@ -20615,7 +20636,7 @@ async function wakeAuthorOnChanges(deps, snapshot, now, droneCap) {
20615
20636
  if (deps.resourcesOk && !deps.resourcesOk()) return false;
20616
20637
  if (countActiveDrones(deps.db) >= droneCap) return false;
20617
20638
  if (!await projectQuotaGateAllows(deps, run2)) return false;
20618
- if (run2.review_cycle >= REVIEW_CYCLE_LIMIT) return false;
20639
+ if (run2.review_cycle >= effectiveReviewCycleLimit()) return false;
20619
20640
  const prUrl = run2.pr_url;
20620
20641
  const events = deps.db.events(run2.id);
20621
20642
  const mark = `${CHANGES_WAKE_ESCALATION_MARK} (\u043A\u0440\u0443\u0433 ${run2.review_cycle ?? 0})`;
@@ -22284,7 +22305,7 @@ async function tickOnce(deps) {
22284
22305
  // рано, спавнить второго нельзя.
22285
22306
  liveOrWaitingReviewers(deps.db, run2.id).length === 0;
22286
22307
  if (reviewerGatesOk && deps.spawnReviewer && await projectWantsChecks(deps, run2)) {
22287
- if (run2.review_cycle >= REVIEW_CYCLE_LIMIT) {
22308
+ if (run2.review_cycle >= effectiveReviewCycleLimit()) {
22288
22309
  if (!deps.db.events(run2.id).some((e) => e.kind === "step" && (e.text ?? "").includes(REVIEW_CYCLE_ESCALATION_MARK))) {
22289
22310
  deps.db.addEvent(run2.id, "step", `${REVIEW_CYCLE_ESCALATION_MARK}: ${run2.review_cycle} \u0446\u0438\u043A\u043B(\u043E\u0432) \u0440\u0435\u0432\u044C\u044E-\u0444\u0438\u043A\u0441\u043E\u0432 \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`);
22290
22311
  narrate(deps, `\u0420\u0435-\u0440\u0435\u0432\u044C\u044E ${droneLabel(run2)} \u0437\u0430\u0446\u0438\u043A\u043B\u0438\u043B\u043E\u0441\u044C (${run2.review_cycle} \u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A.`);
@@ -23386,9 +23407,8 @@ async function runDaemon(opts) {
23386
23407
  recordModelRef: (runId, ref, run2) => {
23387
23408
  if (runModelRefPin.size > 4096) runModelRefPin.clear();
23388
23409
  runModelRefPin.set(runId, ref);
23389
- if (run2 && !run2.model && !isReviewKind(run2.kind)) {
23390
- db.updateRun(runId, { model: `${ref.providerId}/${ref.model}` });
23391
- }
23410
+ const model = modelRefToRecord(run2, ref);
23411
+ if (model) db.updateRun(runId, { model });
23392
23412
  },
23393
23413
  maxCostUsd: () => lastMaxCostUsd,
23394
23414
  // TASWM-535: поднятый потолок тяжёлого прогона живёт в его ленте — спавн читает её
@@ -23574,6 +23594,7 @@ async function runDaemon(opts) {
23574
23594
  lastSwarmModels = settings.models;
23575
23595
  lastSwarmEfforts = settings.efforts;
23576
23596
  lastMaxCostUsd = settings.maxCostUsd;
23597
+ configureReviewCycleLimit(settings.reviewCycleLimit);
23577
23598
  }
23578
23599
  return settings?.maxDrones ?? null;
23579
23600
  },
@@ -24640,6 +24661,8 @@ export {
24640
24661
  buildVerifierPrompt,
24641
24662
  cancelRun,
24642
24663
  capacityProvenance,
24664
+ configureReviewCycleLimit,
24665
+ effectiveReviewCycleLimit,
24643
24666
  hasLiveDescendant,
24644
24667
  isSubrun,
24645
24668
  lastChangesSummary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taskless-app/swarm",
3
- "version": "0.6.64",
3
+ "version": "0.6.66",
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": [
@@ -39,7 +39,7 @@
39
39
  "execa": "^10.0.0",
40
40
  "zod": "3.25.76"
41
41
  },
42
- "gitHead": "7f27042a00bf1554798cce7b8a233ee6c5686a50",
42
+ "gitHead": "a6bc0ea303cb5336fd4e130fba73d90365268b6f",
43
43
  "scripts": {
44
44
  "overmind": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/overmind.ts",
45
45
  "swarm": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/cli.ts",