@taskless-app/swarm 0.6.56 → 0.6.58

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
@@ -3520,7 +3520,7 @@ var init_package = __esm({
3520
3520
  "package.json"() {
3521
3521
  package_default = {
3522
3522
  name: "@taskless-app/swarm",
3523
- version: "0.6.56",
3523
+ version: "0.6.58",
3524
3524
  type: "module",
3525
3525
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
3526
3526
  keywords: [
@@ -3571,7 +3571,7 @@ var init_package = __esm({
3571
3571
  execa: "^10.0.0",
3572
3572
  zod: "3.25.76"
3573
3573
  },
3574
- gitHead: "1922a9b8079c0af2b2b8a9b5fc8756b0218f7446"
3574
+ gitHead: "31bef5768382a2e72ea43807a0f1c61eb9f01071"
3575
3575
  };
3576
3576
  }
3577
3577
  });
package/dist/overmind.js CHANGED
@@ -12593,7 +12593,7 @@ function parkReasonUnretriable(reason) {
12593
12593
  // package.json
12594
12594
  var package_default = {
12595
12595
  name: "@taskless-app/swarm",
12596
- version: "0.6.56",
12596
+ version: "0.6.58",
12597
12597
  type: "module",
12598
12598
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
12599
12599
  keywords: [
@@ -12644,7 +12644,7 @@ var package_default = {
12644
12644
  execa: "^10.0.0",
12645
12645
  zod: "3.25.76"
12646
12646
  },
12647
- gitHead: "1922a9b8079c0af2b2b8a9b5fc8756b0218f7446"
12647
+ gitHead: "31bef5768382a2e72ea43807a0f1c61eb9f01071"
12648
12648
  };
12649
12649
 
12650
12650
  // src/version.ts
@@ -19676,9 +19676,12 @@ async function resurrectStormDead(deps, worktreeExists, hasTranscript, opts) {
19676
19676
  }
19677
19677
  return resurrected;
19678
19678
  }
19679
- async function wakeRun(deps, runId) {
19679
+ async function wakeRun(deps, runId, opts = {}) {
19680
19680
  const run = deps.db.getRun(runId);
19681
19681
  if (!run) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u0432 \u0437\u0435\u0440\u043A\u0430\u043B\u0435 \u0440\u043E\u044F` };
19682
+ if (isAwaitingBudget(run.status, run.stop_reason) && !opts.budgetApproved) {
19683
+ return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0436\u0434\u0451\u0442 \u043E\u0442\u0432\u0435\u0442\u0430 \u043D\u0430 \u0432\u043E\u043F\u0440\u043E\u0441 \u043E \u0431\u044E\u0434\u0436\u0435\u0442\u0435 \u2014 \u043E\u0442\u0432\u0435\u0442\u044C \u043D\u0430 \u043D\u0435\u0433\u043E \xAB\u0434\u0430\xBB \u0438\u043B\u0438 \xAB\u043D\u0435\u0442\xBB, \u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u0431\u0435\u0437 \u043E\u0442\u0432\u0435\u0442\u0430 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u0430 \u0431\u044B \u043E\u0433\u0440\u044B\u0437\u043E\u043A \u0431\u044E\u0434\u0436\u0435\u0442\u0430` };
19684
+ }
19682
19685
  if (run.pid != null && deps.isAlive(run.pid)) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0443\u0436\u0435 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 (pid ${run.pid}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E` };
19683
19686
  if (isTerminalStatus(run.status)) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435\u043D (${run.status}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E` };
19684
19687
  const stoppedForHuman = deps.db.events(run.id).some((e) => isEscalationText(e.text ?? "", run, "author"));
@@ -20042,8 +20045,10 @@ async function spawnFreshReviewerAfterStall(deps, run, why) {
20042
20045
  deps.db.addEvent(run.id, "step", `\u0441\u0432\u0435\u0436\u0438\u0439 \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u043F\u043E\u0441\u043B\u0435 \u043F\u043E\u0442\u0435\u0440\u0438 \u0441\u0435\u0441\u0441\u0438\u0438 \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0441\u044F: ${String(e)}`);
20043
20046
  }
20044
20047
  }
20045
- async function wakeAuthorOnChanges(deps, run, now, droneCap) {
20048
+ async function wakeAuthorOnChanges(deps, snapshot, now, droneCap) {
20046
20049
  if (!deps.respawn) return false;
20050
+ const run = deps.db.getRun(snapshot.id);
20051
+ if (!run || isAwaitingBudget(run.status, run.stop_reason)) return false;
20047
20052
  if (run.pid != null && deps.isAlive(run.pid)) return false;
20048
20053
  if (deps.db.activeReviewersFor(run.id).some((r) => r.status !== "stalled" && !isAwaitingCi(r.status, r.stop_reason))) {
20049
20054
  return false;
@@ -20084,6 +20089,28 @@ async function wakeAuthorOnChanges(deps, run, now, droneCap) {
20084
20089
  );
20085
20090
  }
20086
20091
  }
20092
+ const budgetBase = budgetBaseUsd(deps);
20093
+ const budgetLeft = effectiveCostCeilingUsd(budgetBase, events) - run.cost_usd;
20094
+ if (budgetBase > 0 && budgetLeft < budgetBase * FIX_BUDGET_MIN_SHARE) {
20095
+ const next = nextApprovedCeilingUsd(budgetBase, events, run.cost_usd);
20096
+ if (next == null || next - run.cost_usd < budgetBase * FIX_BUDGET_MIN_SHARE) {
20097
+ return escalate(
20098
+ "\u043D\u0430 \u043F\u0440\u0430\u0432\u043A\u0438 \u043D\u0435 \u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C \u0431\u044E\u0434\u0436\u0435\u0442\u0430, \u0430 \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u0442\u044C \u0443\u0436\u0435 \u043D\u0435\u043A\u0443\u0434\u0430 \u2014 \u043F\u0440\u0435\u0434\u0435\u043B \u0442\u0440\u0451\u0445 \u0431\u0430\u0437\u043E\u0432\u044B\u0445 \u043F\u043E\u0442\u043E\u043B\u043A\u043E\u0432",
20099
+ `${droneLabel(run)} \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0432\u0437\u044F\u0442\u044C\u0441\u044F \u0437\u0430 \u043F\u0440\u0430\u0432\u043A\u0438: \u0431\u044E\u0434\u0436\u0435\u0442 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u0432\u044B\u0431\u0440\u0430\u043D \u0434\u043E \u043F\u0440\u0435\u0434\u0435\u043B\u0430. \u0414\u043E\u0434\u0435\u043B\u0430\u0439 \u0440\u0443\u043A\u0430\u043C\u0438.`
20100
+ );
20101
+ }
20102
+ const ceiling = effectiveCostCeilingUsd(budgetBase, events);
20103
+ const left = `$${Math.max(0, budgetLeft).toFixed(2)}`;
20104
+ askBudget(
20105
+ deps,
20106
+ run,
20107
+ `${AWAITING_BUDGET_REASON} \u2014 ${FIX_BUDGET_MARK} (\u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C ${left} \u043F\u0440\u0438 \u043F\u043E\u0442\u043E\u043B\u043A\u0435 $${ceiling.toFixed(2)})`,
20108
+ fixBudgetQuestionText({ leftUsd: Math.max(0, budgetLeft), ceilingUsd: ceiling, nextUsd: next, prUrl })
20109
+ );
20110
+ deps.db.addEvent(run.id, "paused", `${AWAITING_BUDGET_REASON} \u2014 ${FIX_BUDGET_MARK}: \u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C ${left}, \u0441\u043F\u0440\u043E\u0441\u0438\u043B, \u0434\u0430\u0442\u044C \u043B\u0438 \u0431\u044E\u0434\u0436\u0435\u0442`);
20111
+ narrate(deps, `${droneLabel(run)}: \u043D\u0430 \u043F\u0440\u0430\u0432\u043A\u0438 \u043F\u043E \u0440\u0435\u0432\u044C\u044E \u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C ${left} \u2014 \u0441\u043F\u0440\u043E\u0441\u0438\u043B, \u0434\u0430\u0442\u044C \u043B\u0438 \u0431\u044E\u0434\u0436\u0435\u0442, \u043F\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043C \u0431\u0440\u0430\u0442\u044C\u0441\u044F.`);
20112
+ return false;
20113
+ }
20087
20114
  const wt = run.worktree_path;
20088
20115
  const wtPath = wt || (deps.worktreesDir ? droneWorktreePath(deps.worktreesDir, normalizeRepo(run.repo || deps.repoFallback || ""), run.task_slug) : null);
20089
20116
  const worktreeReady = !!wtPath && (deps.worktreeExists ?? existsSync20)(wtPath);
@@ -21202,8 +21229,17 @@ async function handleBudgetExhausted(deps, run, ctx) {
21202
21229
  narrate(deps, `${droneLabel(run)} \u0443\u043F\u0451\u0440\u0441\u044F \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442 (${spent}) \u2014 \u0437\u0430\u043C\u0435\u0440, \u0440\u0430\u0431\u043E\u0442\u0430 \u0432 worktree \u0446\u0435\u043B\u0430, \u0434\u0430\u043B\u044C\u0448\u0435 \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u0437\u0430 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u043E\u043C.`);
21203
21230
  }
21204
21231
  var BUDGET_QUESTION_PREFIX = "\u0423\u043F\u0451\u0440\u0441\u044F \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442";
21232
+ var FIX_BUDGET_MIN_SHARE = 0.25;
21233
+ var FIX_BUDGET_MARK = "\u043D\u0430 \u043F\u0440\u0430\u0432\u043A\u0438 \u043F\u043E \u0440\u0435\u0432\u044C\u044E";
21234
+ var FIX_BUDGET_QUESTION_PREFIX = "\u0411\u044E\u0434\u0436\u0435\u0442 \u043D\u0430 \u043F\u0440\u0430\u0432\u043A\u0438";
21235
+ function fixBudgetQuestionText(q, unclearAnswer) {
21236
+ return [
21237
+ unclearAnswer != null ? `${FIX_BUDGET_QUESTION_PREFIX}: \u043E\u0442\u0432\u0435\u0442 \xAB${unclearAnswer}\xBB \u043D\u0435 \u043F\u043E\u043D\u044F\u043B, \u043D\u0443\u0436\u0435\u043D \u044F\u0441\u043D\u044B\u0439 \xAB\u0434\u0430\xBB \u0438\u043B\u0438 \xAB\u043D\u0435\u0442\xBB.` : `${FIX_BUDGET_QUESTION_PREFIX}: \u0440\u0435\u0432\u044C\u044E \u043F\u0440\u043E\u0441\u0438\u0442 \u043F\u0440\u0430\u0432\u043A\u0438 (${q.prUrl}), \u0430 \u043D\u0430 \u043D\u0438\u0445 \u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C $${q.leftUsd.toFixed(2)} \u043F\u0440\u0438 \u043F\u043E\u0442\u043E\u043B\u043A\u0435 $${q.ceilingUsd.toFixed(2)} \u2014 \u043D\u0435 \u0445\u0432\u0430\u0442\u0438\u0442.`,
21238
+ `\u0414\u0430\u0442\u044C \u0431\u044E\u0434\u0436\u0435\u0442? \u041E\u0442\u0432\u0435\u0442\u044C \xAB\u0434\u0430\xBB \u2014 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0441\u0442\u0430\u043D\u0435\u0442 $${q.nextUsd.toFixed(2)}, \u0438 \u044F \u0432\u043E\u0437\u044C\u043C\u0443\u0441\u044C \u0437\u0430 \u043F\u0440\u0430\u0432\u043A\u0438. \xAB\u041D\u0435\u0442\xBB \u2014 \u043E\u0441\u0442\u0430\u0432\u043B\u044E PR \u043D\u0430 \u0440\u0435\u0432\u044C\u044E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443.`
21239
+ ].join(" ");
21240
+ }
21205
21241
  function isBudgetQuestion(text) {
21206
- return text.startsWith(BUDGET_QUESTION_PREFIX);
21242
+ return text.startsWith(BUDGET_QUESTION_PREFIX) || text.startsWith(FIX_BUDGET_QUESTION_PREFIX);
21207
21243
  }
21208
21244
  function budgetQuestionText(q, unclearAnswer) {
21209
21245
  return [
@@ -21247,6 +21283,10 @@ async function answerBudgetQuestions(deps, ctx) {
21247
21283
  const answer = q.answer.trim();
21248
21284
  const spent = `$${run.cost_usd.toFixed(2)}`;
21249
21285
  const verdict = budgetAnswer(answer);
21286
+ if (run.stop_reason?.includes(FIX_BUDGET_MARK)) {
21287
+ await answerFixBudget(deps, run, q, answer, verdict);
21288
+ continue;
21289
+ }
21250
21290
  if (verdict === "no") {
21251
21291
  const reason = `${BUDGET_STOP_REASON} (\u043F\u043E\u0442\u0440\u0430\u0447\u0435\u043D\u043E ${spent}) \u2014 \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442\u044C \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u043B\u0438`;
21252
21292
  if (run.worktree_path) await deps.removeWorktree(run.repo, run.worktree_path).catch(() => {
@@ -21291,7 +21331,8 @@ async function answerBudgetQuestions(deps, ctx) {
21291
21331
  ...deps.hasTranscript ? { hasTranscript: deps.hasTranscript } : {},
21292
21332
  narrate: (m) => narrate(deps, m)
21293
21333
  },
21294
- run.id
21334
+ run.id,
21335
+ { budgetApproved: true }
21295
21336
  );
21296
21337
  if (woken.ok) {
21297
21338
  budgetSyncAt.delete(run.id);
@@ -21301,6 +21342,37 @@ async function answerBudgetQuestions(deps, ctx) {
21301
21342
  if (deps.db.events(run.id).at(-1)?.text !== note) deps.db.addEvent(run.id, "step", note);
21302
21343
  }
21303
21344
  }
21345
+ async function answerFixBudget(deps, run, q, answer, verdict) {
21346
+ const base3 = budgetBaseUsd(deps);
21347
+ const events = deps.db.events(run.id);
21348
+ const mark = `${CHANGES_WAKE_ESCALATION_MARK} (\u043A\u0440\u0443\u0433 ${run.review_cycle ?? 0})`;
21349
+ const backToReview = (humanNote) => {
21350
+ deps.db.updateRun(run.id, { status: "review", stop_reason: null });
21351
+ if (humanNote) deps.db.addEvent(run.id, "step", `${mark}: ${humanNote} \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`);
21352
+ budgetSyncAt.delete(run.id);
21353
+ };
21354
+ if (!(base3 > 0)) return backToReview();
21355
+ const next = nextApprovedCeilingUsd(base3, events, run.cost_usd);
21356
+ if (verdict === "no") {
21357
+ backToReview(`\u043D\u0430 \u043F\u0440\u0430\u0432\u043A\u0438 \u043D\u0435 \u0434\u0430\u043B\u0438 \u0431\u044E\u0434\u0436\u0435\u0442\u0430 (\xAB${answer}\xBB)`);
21358
+ narrate(deps, `${droneLabel(run)}: \u043D\u0430 \u043F\u0440\u0430\u0432\u043A\u0438 \u0431\u044E\u0434\u0436\u0435\u0442\u0430 \u043D\u0435 \u0434\u0430\u043B\u0438 \u2014 PR \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u043D\u0430 \u0440\u0435\u0432\u044C\u044E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443.`);
21359
+ return;
21360
+ }
21361
+ if (verdict === "unclear") {
21362
+ if (next == null) return backToReview("\u043D\u0430 \u043F\u0440\u0430\u0432\u043A\u0438 \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u0442\u044C \u0443\u0436\u0435 \u043D\u0435\u043A\u0443\u0434\u0430 \u2014 \u043F\u0440\u0435\u0434\u0435\u043B \u0442\u0440\u0451\u0445 \u0431\u0430\u0437\u043E\u0432\u044B\u0445 \u043F\u043E\u0442\u043E\u043B\u043A\u043E\u0432");
21363
+ const ceiling = effectiveCostCeilingUsd(base3, events);
21364
+ const left = Math.max(0, ceiling - run.cost_usd);
21365
+ askBudget(deps, run, run.stop_reason, fixBudgetQuestionText({ leftUsd: left, ceilingUsd: ceiling, nextUsd: next, prUrl: run.pr_url ?? "" }, answer));
21366
+ return;
21367
+ }
21368
+ const askedMs = stampMs(q.asked_at);
21369
+ if (!events.some((e) => isBudgetApprovedEvent(e) && stampMs(e.at) >= askedMs)) {
21370
+ if (next == null) return backToReview("\u043D\u0430 \xAB\u0434\u0430\xBB \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u0442\u044C \u0443\u0436\u0435 \u043D\u0435\u043A\u0443\u0434\u0430 \u2014 \u043F\u0440\u0435\u0434\u0435\u043B \u0442\u0440\u0451\u0445 \u0431\u0430\u0437\u043E\u0432\u044B\u0445 \u043F\u043E\u0442\u043E\u043B\u043A\u043E\u0432");
21371
+ deps.db.addEvent(run.id, "note", budgetApprovedText(next, answer));
21372
+ }
21373
+ backToReview();
21374
+ narrate(deps, `${droneLabel(run)}: \u043D\u0430 \u043F\u0440\u0430\u0432\u043A\u0438 \u0434\u0430\u043B\u0438 \u0431\u044E\u0434\u0436\u0435\u0442 \u2014 \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u044E \u043D\u0430 \u0440\u0435\u0432\u044C\u044E, \u043F\u0440\u0430\u0432\u043A\u0430 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442\u0441\u044F \u0441\u0432\u043E\u0438\u043C \u0445\u043E\u0434\u043E\u043C.`);
21375
+ }
21304
21376
  function buildEscalationCandidates(db) {
21305
21377
  const candidates = [];
21306
21378
  for (const run of db.listRuns("review")) {
@@ -21638,6 +21710,7 @@ async function tickOnce(deps) {
21638
21710
  deps.returners?.delete(run.id);
21639
21711
  continue;
21640
21712
  }
21713
+ if (deps.db.getRun(run.id)?.status !== "review") continue;
21641
21714
  if (capFull) noteReturner(deps, run, now, { kind: "changes", since: now });
21642
21715
  }
21643
21716
  const reviewerGatesOk = !st.conflicting && !st.isDraft && !!deps.spawnReviewer && !stormActive && (!deps.reviewCutoffAt || run.updated_at >= deps.reviewCutoffAt) && deps.db.reviewerCountFor(run.id) === 0 && // ГЕЙТ ПЕРВЫМ: живой/успешный ревьюер уже есть — ни капа, ни спавна не трогаем
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taskless-app/swarm",
3
- "version": "0.6.56",
3
+ "version": "0.6.58",
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": "1922a9b8079c0af2b2b8a9b5fc8756b0218f7446",
42
+ "gitHead": "31bef5768382a2e72ea43807a0f1c61eb9f01071",
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",