@taskless-app/swarm 0.6.16 → 0.6.18

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
@@ -1455,6 +1455,14 @@ var init_queuePlan = __esm({
1455
1455
  }
1456
1456
  });
1457
1457
 
1458
+ // ../swarm-core/src/zoneMemory.ts
1459
+ var init_zoneMemory = __esm({
1460
+ "../swarm-core/src/zoneMemory.ts"() {
1461
+ "use strict";
1462
+ init_time();
1463
+ }
1464
+ });
1465
+
1458
1466
  // ../swarm-core/src/index.ts
1459
1467
  var init_src = __esm({
1460
1468
  "../swarm-core/src/index.ts"() {
@@ -1485,6 +1493,7 @@ var init_src = __esm({
1485
1493
  init_codeZones();
1486
1494
  init_effort();
1487
1495
  init_queuePlan();
1496
+ init_zoneMemory();
1488
1497
  }
1489
1498
  });
1490
1499
 
@@ -2275,7 +2284,7 @@ var init_package = __esm({
2275
2284
  "package.json"() {
2276
2285
  package_default = {
2277
2286
  name: "@taskless-app/swarm",
2278
- version: "0.6.16",
2287
+ version: "0.6.18",
2279
2288
  type: "module",
2280
2289
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
2281
2290
  keywords: [
@@ -2325,7 +2334,7 @@ var init_package = __esm({
2325
2334
  execa: "^10.0.0",
2326
2335
  zod: "3.25.76"
2327
2336
  },
2328
- gitHead: "785b1478f2b81406c9b6524913939707b6c096fb"
2337
+ gitHead: "9531f684d8a7c6c4c3595f0c57e4aebce0038ac1"
2329
2338
  };
2330
2339
  }
2331
2340
  });
@@ -6742,6 +6751,27 @@ var SwarmClient = class _SwarmClient {
6742
6751
  const sc = res.structuredContent ?? {};
6743
6752
  return parsePendingRepoRules(sc.rules);
6744
6753
  }
6754
+ /**
6755
+ * ПАМЯТЬ ЗОНЫ (TASWM-466) из того же swarm_repo_config — отдельное поле `zoneMemory`,
6756
+ * карта «зона → заметки», только принятое вердиктом. Демон кладёт заметки НУЖНОЙ зоны в
6757
+ * промпт дрона (см. zoneNotes.ts), поэтому отдаём карту КАК ЕСТЬ, сырым объектом: разбор
6758
+ * и выбор зоны — одно место, и оно рядом с рендером блока.
6759
+ *
6760
+ * Пустая карта — памяти нет / поля в ответе прода нет (прод старее демона) / repo не
6761
+ * смэплен / прод недоступен. Все четыре случая РАБОЧИЕ: промпт собирается как раньше.
6762
+ */
6763
+ async repoZoneMemory(repoFullName) {
6764
+ let res;
6765
+ try {
6766
+ res = await this.call("swarm_repo_config", { repoFullName });
6767
+ } catch {
6768
+ return {};
6769
+ }
6770
+ if (res?.isError) return {};
6771
+ const sc = res.structuredContent ?? {};
6772
+ const m = sc.zoneMemory;
6773
+ return m && typeof m === "object" && !Array.isArray(m) ? m : {};
6774
+ }
6745
6775
  /**
6746
6776
  * Live КОМАНДЫ ПОДГОТОВКИ worktree из Taskless (TASWM-312): тот же swarm_repo_config по
6747
6777
  * repoFullName — Project.swarmConfig.prepare. Настройка сильнее детекта демона (deps.ts):
package/dist/overmind.js CHANGED
@@ -1527,6 +1527,16 @@ var init_queuePlan = __esm({
1527
1527
  }
1528
1528
  });
1529
1529
 
1530
+ // ../swarm-core/src/zoneMemory.ts
1531
+ var ZONE_NOTES_PER_RUN_MAX;
1532
+ var init_zoneMemory = __esm({
1533
+ "../swarm-core/src/zoneMemory.ts"() {
1534
+ "use strict";
1535
+ init_time();
1536
+ ZONE_NOTES_PER_RUN_MAX = 2;
1537
+ }
1538
+ });
1539
+
1530
1540
  // ../swarm-core/src/index.ts
1531
1541
  var init_src = __esm({
1532
1542
  "../swarm-core/src/index.ts"() {
@@ -1557,6 +1567,7 @@ var init_src = __esm({
1557
1567
  init_codeZones();
1558
1568
  init_effort();
1559
1569
  init_queuePlan();
1570
+ init_zoneMemory();
1560
1571
  }
1561
1572
  });
1562
1573
 
@@ -4606,6 +4617,29 @@ function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl
4606
4617
  const text = res.content?.find((c) => c.type === "text")?.text;
4607
4618
  return typeof text === "string" && text.trim() ? text : null;
4608
4619
  }
4620
+ async function proposeZoneNotes(input) {
4621
+ const res = await safeCall("swarm_zone_note_propose", {
4622
+ repoFullName: input.repoFullName,
4623
+ notes: input.notes,
4624
+ taskSlug: input.taskSlug,
4625
+ ...input.by ? { by: input.by } : {},
4626
+ ...input.prUrl ? { prUrl: input.prUrl } : {}
4627
+ });
4628
+ if (!res || res.isError) return null;
4629
+ const text = res.content?.find((c) => c.type === "text")?.text;
4630
+ return typeof text === "string" && text.trim() ? text : null;
4631
+ }
4632
+ async function confirmZoneNotes(input) {
4633
+ const res = await safeCall("swarm_zone_note_confirm", {
4634
+ repoFullName: input.repoFullName,
4635
+ notes: input.notes,
4636
+ ...input.zone ? { zone: input.zone } : {},
4637
+ ...input.by ? { by: input.by } : {}
4638
+ });
4639
+ if (!res || res.isError) return null;
4640
+ const text = res.content?.find((c) => c.type === "text")?.text;
4641
+ return typeof text === "string" && text.trim() ? text : null;
4642
+ }
4609
4643
  async function resolveId(idOrSlug) {
4610
4644
  const cached = idCache.get(idOrSlug);
4611
4645
  if (cached) return cached;
@@ -4688,6 +4722,8 @@ function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl
4688
4722
  reportAcceptanceCheck,
4689
4723
  proposeRepoRules,
4690
4724
  decideRepoRules,
4725
+ proposeZoneNotes,
4726
+ confirmZoneNotes,
4691
4727
  createTask: async (input) => {
4692
4728
  if (!input.projectId) {
4693
4729
  throw new Error("createTask: projectId \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u0435\u043D \u2014 \u0430\u0432\u0442\u043E-\u0441\u043E\u0437\u0434\u0430\u043D\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0440\u043E\u0435\u043C \u0431\u0435\u0437 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0437\u0430\u043F\u0440\u0435\u0449\u0435\u043D\u043E (TASWM-204)");
@@ -7641,6 +7677,27 @@ var SwarmClient = class _SwarmClient {
7641
7677
  const sc = res.structuredContent ?? {};
7642
7678
  return parsePendingRepoRules(sc.rules);
7643
7679
  }
7680
+ /**
7681
+ * ПАМЯТЬ ЗОНЫ (TASWM-466) из того же swarm_repo_config — отдельное поле `zoneMemory`,
7682
+ * карта «зона → заметки», только принятое вердиктом. Демон кладёт заметки НУЖНОЙ зоны в
7683
+ * промпт дрона (см. zoneNotes.ts), поэтому отдаём карту КАК ЕСТЬ, сырым объектом: разбор
7684
+ * и выбор зоны — одно место, и оно рядом с рендером блока.
7685
+ *
7686
+ * Пустая карта — памяти нет / поля в ответе прода нет (прод старее демона) / repo не
7687
+ * смэплен / прод недоступен. Все четыре случая РАБОЧИЕ: промпт собирается как раньше.
7688
+ */
7689
+ async repoZoneMemory(repoFullName) {
7690
+ let res;
7691
+ try {
7692
+ res = await this.call("swarm_repo_config", { repoFullName });
7693
+ } catch {
7694
+ return {};
7695
+ }
7696
+ if (res?.isError) return {};
7697
+ const sc = res.structuredContent ?? {};
7698
+ const m = sc.zoneMemory;
7699
+ return m && typeof m === "object" && !Array.isArray(m) ? m : {};
7700
+ }
7644
7701
  /**
7645
7702
  * Live КОМАНДЫ ПОДГОТОВКИ worktree из Taskless (TASWM-312): тот же swarm_repo_config по
7646
7703
  * repoFullName — Project.swarmConfig.prepare. Настройка сильнее детекта демона (deps.ts):
@@ -10625,6 +10682,54 @@ init_shotUpload();
10625
10682
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10626
10683
  import { z } from "zod";
10627
10684
  init_git();
10685
+
10686
+ // src/zoneNotes.ts
10687
+ init_src();
10688
+ function parseZoneMemory(raw, zone) {
10689
+ const key = normalizeZoneLabel(zone);
10690
+ if (!key || !raw || typeof raw !== "object" || Array.isArray(raw)) return [];
10691
+ const notes = raw[key];
10692
+ if (!Array.isArray(notes)) return [];
10693
+ const out = [];
10694
+ for (const item of notes) {
10695
+ if (!item || typeof item !== "object") continue;
10696
+ const n = item;
10697
+ const id = typeof n.id === "string" ? n.id : "";
10698
+ const claim = typeof n.claim === "string" ? n.claim.trim() : "";
10699
+ if (id && claim) out.push({ id, claim });
10700
+ }
10701
+ return out;
10702
+ }
10703
+ function normalizeZoneLabel(zone) {
10704
+ return (zone ?? "").trim().replace(/^area:/i, "").trim().toLowerCase();
10705
+ }
10706
+ function renderZoneMemoryBlock(zone, notes) {
10707
+ const label = normalizeZoneLabel(zone);
10708
+ if (!label) return "";
10709
+ const head = notes.length ? [
10710
+ `\u{1F9E0} \u041F\u0410\u041C\u042F\u0422\u042C \u0417\u041E\u041D\u042B \`${label}\` \u2014 \u0447\u0442\u043E \u0440\u043E\u0439 \u0443\u0436\u0435 \u0432\u044B\u044F\u0441\u043D\u0438\u043B \u043F\u0440\u043E \u044D\u0442\u043E\u0442 \u043A\u0443\u0441\u043E\u043A \u043A\u043E\u0434\u0430 (\u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u043E\u043C, \u043D\u0435 \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0437 \u043A\u043E\u0434\u0430):`,
10711
+ ...notes.map((n) => `\u2022 [${n.id}] ${n.claim}`),
10712
+ "\u042D\u0442\u043E \u043F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0438, \u0430 \u043D\u0435 \u0438\u0441\u0442\u0438\u043D\u0430 \u0432 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0439 \u0438\u043D\u0441\u0442\u0430\u043D\u0446\u0438\u0438: \u0440\u0430\u0437\u043E\u0448\u043B\u043E\u0441\u044C \u0441 \u043A\u043E\u0434\u043E\u043C \u2014 \u0432\u0435\u0440\u044C \u043A\u043E\u0434\u0443 \u0438 \u0441\u043A\u0430\u0436\u0438 \u043E\u0431 \u044D\u0442\u043E\u043C \u0432 \u0441\u0434\u0430\u0447\u0435.",
10713
+ `\u041F\u0440\u0438\u0433\u043E\u0434\u0438\u043B\u0430\u0441\u044C \u0437\u0430\u043C\u0435\u0442\u043A\u0430 \u2014 \u043D\u0430\u0437\u043E\u0432\u0438 \u0435\u0451 \u0432 done: \`zone_notes_used: ["${notes[0].id}"]\` (id \u0438\u0437 \u0441\u043F\u0438\u0441\u043A\u0430 \u0432\u044B\u0448\u0435 \u0438\u043B\u0438 \u0441\u0430\u043C \u0442\u0435\u043A\u0441\u0442 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F). \u042D\u0442\u043E \u0435\u0434\u0438\u043D\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0435, \u0447\u0435\u043C \u0437\u0430\u043C\u0435\u0442\u043A\u0430 \u0436\u0438\u0432\u0451\u0442: \u043F\u0430\u043C\u044F\u0442\u044C \u0437\u043E\u043D\u044B \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u0430, \u0438 \u0432\u044B\u0442\u0435\u0441\u043D\u044F\u0435\u0442\u0441\u044F \u0442\u0430, \u0432 \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u0440\u043E\u0439 \u0434\u043E\u043B\u044C\u0448\u0435 \u0432\u0441\u0435\u0445 \u043D\u0435 \u0443\u0431\u0435\u0436\u0434\u0430\u043B\u0441\u044F. \u041D\u0435 \u043F\u0440\u0438\u0433\u043E\u0434\u0438\u043B\u043E\u0441\u044C \u043D\u0438\u0447\u0435\u0433\u043E \u2014 \u043D\u0435 \u043D\u0430\u0437\u044B\u0432\u0430\u0439 \u043D\u0438\u0447\u0435\u0433\u043E, \u044D\u0442\u043E \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0438\u0441\u0445\u043E\u0434.`
10714
+ ] : [`\u{1F9E0} \u041F\u0410\u041C\u042F\u0422\u042C \u0417\u041E\u041D\u042B \`${label}\` \u043F\u0443\u0441\u0442\u0430 \u2014 \u0440\u043E\u0439 \u043F\u0440\u043E \u044D\u0442\u043E\u0442 \u043A\u0443\u0441\u043E\u043A \u043A\u043E\u0434\u0430 \u043F\u043E\u043A\u0430 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B.`];
10715
+ return [
10716
+ ...head,
10717
+ `\u{1F9E0} \u0417\u0410\u041C\u0415\u0422\u041A\u0410 \u0412 \u041F\u0410\u041C\u042F\u0422\u042C \u0417\u041E\u041D\u042B \u2014 \u0432 \u043A\u043E\u043D\u0446\u0435 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0438 \u0434\u043E ${ZONE_NOTES_PER_RUN_MAX} \u0448\u0442\u0443\u043A: \`zone_notes: [{ zone: "${label}", claim: "\u2026", why: "\u2026" }]\` (zone \u043C\u043E\u0436\u043D\u043E \u043D\u0435 \u0443\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C \u2014 \u0432\u043E\u0437\u044C\u043C\u0451\u0442\u0441\u044F \u0442\u0432\u043E\u044F). \u0427\u0422\u041E \u0433\u043E\u0434\u0438\u0442\u0441\u044F: \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u0441 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439 (\xAB\u043E\u0447\u0435\u0440\u0435\u0434\u044C \u0447\u0438\u0442\u0430\u044E\u0442 \u0438\u0437 \u043E\u0434\u043D\u043E\u0439 \u0442\u043E\u0447\u043A\u0438, \u043F\u043E\u0442\u043E\u043C\u0443 \u0447\u0442\u043E \u2026\xBB), \u0433\u0440\u0430\u0431\u043B\u0438 \u0441 \u0446\u0435\u043D\u043E\u0439 (\xAB\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043D\u0430\u043A\u0430\u0442\u044B\u0432\u0430\u044E\u0442\u0441\u044F \u043D\u0435 \u0442\u0435\u043C, \u0447\u0435\u043C \u043A\u0430\u0436\u0435\u0442\u0441\u044F, \u2014 \u0441\u0442\u043E\u0438\u043B\u043E \u0447\u0430\u0441\u0430\xBB), \u043E\u0442\u0431\u0440\u043E\u0448\u0435\u043D\u043D\u044B\u0439 \u0432\u0430\u0440\u0438\u0430\u043D\u0442 \u0441 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439. \u0427\u0422\u041E \u041D\u0415 \u0433\u043E\u0434\u0438\u0442\u0441\u044F: \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u043E \u043A\u043E\u0434\u0430 (\xAB\u0445\u0435\u043D\u0434\u043B\u0435\u0440 \u043B\u0435\u0436\u0438\u0442 \u0432 handlers.ts\xBB, \xAB\u044D\u0442\u0430 \u0444\u0443\u043D\u043A\u0446\u0438\u044F \u0434\u0435\u043B\u0430\u0435\u0442 \u0442\u043E-\u0442\u043E\xBB) \u2014 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0434\u0440\u043E\u043D \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0435\u0442 \u044D\u0442\u043E \u0441\u0430\u043C \u0438 \u0431\u044B\u0441\u0442\u0440\u0435\u0435, \u0447\u0435\u043C \u0442\u0432\u043E\u044E \u0437\u0430\u043C\u0435\u0442\u043A\u0443. \u0420\u0435\u0448\u0430\u0435\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440; \u043D\u0438\u0447\u0435\u0433\u043E \u0442\u0430\u043A\u043E\u0433\u043E \u043D\u0435 \u0431\u044B\u043B\u043E \u2014 \u043D\u0435 \u0432\u044B\u0434\u0443\u043C\u044B\u0432\u0430\u0439, \u043F\u0443\u0441\u0442\u043E\u0439 \u0441\u043F\u0438\u0441\u043E\u043A \u044D\u0442\u043E \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0438\u0441\u0445\u043E\u0434 \u043F\u0440\u043E\u0433\u043E\u043D\u0430.`
10718
+ ].join("\n");
10719
+ }
10720
+ function collectZoneNoteConfirmations(report) {
10721
+ const seen = /* @__PURE__ */ new Set();
10722
+ const out = [];
10723
+ for (const raw of report.used ?? []) {
10724
+ const s = typeof raw === "string" ? raw.trim() : "";
10725
+ if (!s || seen.has(s)) continue;
10726
+ seen.add(s);
10727
+ out.push(s);
10728
+ }
10729
+ return out;
10730
+ }
10731
+
10732
+ // src/droneServer.ts
10628
10733
  init_src();
10629
10734
 
10630
10735
  // src/reviewCycle.ts
@@ -11014,11 +11119,26 @@ ${notFoundText}` : notFoundText }],
11014
11119
  ).optional().describe(
11015
11120
  `\u041F\u0420\u0410\u0412\u0418\u041B\u041E \u041E \u0420\u0415\u041F\u041E\u0417\u0418\u0422\u041E\u0420\u0418\u0418, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u0432 \u043D\u0451\u043C \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E (TASWM-431) \u2014 \u043D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 ${DRONE_RULES_PER_RUN_MAX} \u0437\u0430 \u043F\u0440\u043E\u0433\u043E\u043D \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u0442\u043E, \u043E\u0431\u043E \u0447\u0442\u043E \u0442\u044B \u0420\u0415\u0410\u041B\u042C\u041D\u041E \u0441\u043F\u043E\u0442\u043A\u043D\u0443\u043B\u0441\u044F \u0438 \u043F\u043E\u0442\u0440\u0430\u0442\u0438\u043B \u0432\u0440\u0435\u043C\u044F (\xAB\u0438\u043D\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043E\u043D\u043D\u044B\u0435 \u0442\u0435\u0441\u0442\u044B \u0436\u0434\u0443\u0442 \u043F\u043E\u0441\u0442\u0433\u0440\u0435\u0441 \u043D\u0430 5433\xBB). \u041E\u0431\u0449\u0435\u0435 \u043C\u0435\u0441\u0442\u043E (\xAB\u0442\u0435\u0441\u0442\u044B \u0433\u043E\u043D\u044F\u044E\u0442\u0441\u044F \u0447\u0435\u0440\u0435\u0437 pnpm test\xBB) \u043F\u0440\u0430\u0432\u0438\u043B\u043E\u043C \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F. \u0423\u0436\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043D\u043E\u0435 \u043E\u0442\u0441\u0435\u0435\u0442\u0441\u044F \u0441\u0430\u043C\u043E. \u0420\u0435\u0448\u0430\u0435\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0432 \u0441\u0432\u043E\u0451\u043C \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0435.`
11016
11121
  ),
11122
+ // TASWM-466: память ЗОНЫ — то же, что правило репозитория, но адресат кусок кода.
11123
+ // Форму судит прод (там бюджет и дедуп), здесь схема свободная по той же причине,
11124
+ // что у rules: гейт в двух местах разъехался бы.
11125
+ zone_notes: z.array(
11126
+ z.object({
11127
+ zone: z.string().optional().describe("\u0437\u043E\u043D\u0430 \u043A\u043E\u0434\u0430 (area:*). \u041D\u0435 \u0443\u043A\u0430\u0437\u0430\u043D\u0430 \u2014 \u0432\u043E\u0437\u044C\u043C\u0451\u0442\u0441\u044F \u0437\u043E\u043D\u0430 \u0442\u0432\u043E\u0435\u0433\u043E \u043F\u0440\u043E\u0433\u043E\u043D\u0430"),
11128
+ claim: z.string().describe("\u0441\u0430\u043C\u043E \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435, \u043E\u0434\u043D\u043E\u0439 \u0444\u0440\u0430\u0437\u043E\u0439"),
11129
+ why: z.string().describe("\u043F\u043E\u0447\u0435\u043C\u0443 \u044D\u0442\u043E \u0442\u0430\u043A \u2014 \u0431\u0435\u0437 \u043F\u0440\u0438\u0447\u0438\u043D\u044B \u0437\u0430\u043C\u0435\u0442\u043A\u0430 \u043D\u0435 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F")
11130
+ })
11131
+ ).optional().describe(
11132
+ `\u0417\u0410\u041C\u0415\u0422\u041A\u0410 \u0412 \u041F\u0410\u041C\u042F\u0422\u042C \u0417\u041E\u041D\u042B (TASWM-466) \u2014 \u043D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 ${ZONE_NOTES_PER_RUN_MAX} \u0437\u0430 \u043F\u0440\u043E\u0433\u043E\u043D: \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u0441 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439, \u0433\u0440\u0430\u0431\u043B\u0438 \u0441 \u0446\u0435\u043D\u043E\u0439, \u043E\u0442\u0431\u0440\u043E\u0448\u0435\u043D\u043D\u044B\u0439 \u0432\u0430\u0440\u0438\u0430\u043D\u0442 \u0441 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439. \u0423\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u043E \u043A\u043E\u0434\u0430 (\xAB\u0445\u0435\u043D\u0434\u043B\u0435\u0440 \u043B\u0435\u0436\u0438\u0442 \u0442\u0430\u043C-\u0442\u043E\xBB) \u0437\u0430\u043C\u0435\u0442\u043A\u043E\u0439 \u041D\u0415 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F: \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0434\u0440\u043E\u043D \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0435\u0442 \u044D\u0442\u043E \u0441\u0430\u043C \u0438 \u0431\u044B\u0441\u0442\u0440\u0435\u0435. \u0420\u0435\u0448\u0430\u0435\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440; \u0443\u0436\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043D\u043E\u0435 \u043E\u0442\u0441\u0435\u0435\u0442\u0441\u044F \u0441\u0430\u043C\u043E.`
11133
+ ),
11134
+ zone_notes_used: z.array(z.string()).optional().describe(
11135
+ "\u0417\u0410\u041C\u0415\u0422\u041A\u0418 \u0417\u041E\u041D\u042B, \u041A\u041E\u0422\u041E\u0420\u042B\u0415 \u041F\u0420\u0418\u0413\u041E\u0414\u0418\u041B\u0418\u0421\u042C (TASWM-466): id \u0438\u0437 \u0431\u043B\u043E\u043A\u0430 \xAB\u043F\u0430\u043C\u044F\u0442\u044C \u0437\u043E\u043D\u044B\xBB \u0432 \u0442\u0432\u043E\u0451\u043C \u043F\u0440\u043E\u043C\u043F\u0442\u0435 (\u0438\u043B\u0438 \u0441\u0430\u043C \u0442\u0435\u043A\u0441\u0442 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F). \u0418\u043C \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442\u0441\u044F \u0441\u0447\u0451\u0442\u0447\u0438\u043A \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0439 \u2014 \u044D\u0442\u043E \u0435\u0434\u0438\u043D\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0435, \u0447\u0435\u043C \u0437\u0430\u043C\u0435\u0442\u043A\u0430 \u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044F \u0432 \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u043D\u043E\u0439 \u043F\u0430\u043C\u044F\u0442\u0438 \u0437\u043E\u043D\u044B. \u041D\u0435 \u043F\u0440\u0438\u0433\u043E\u0434\u0438\u043B\u043E\u0441\u044C \u043D\u0438\u0447\u0435\u0433\u043E \u2014 \u043D\u0435 \u043D\u0430\u0437\u044B\u0432\u0430\u0439 \u043D\u0438\u0447\u0435\u0433\u043E."
11136
+ ),
11017
11137
  rule_verdicts: z.array(z.object({ field: z.enum([...RULE_FIELDS]), apply: z.boolean() })).optional().describe(
11018
11138
  "\u0422\u041E\u041B\u042C\u041A\u041E \u0414\u041B\u042F \u0420\u0415\u0412\u042C\u042E\u0415\u0420\u0410 (TASWM-431): \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043F\u043E \u043F\u0440\u0430\u0432\u0438\u043B\u0430\u043C, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043F\u0440\u0438\u043D\u0451\u0441 \u0434\u0440\u043E\u043D (\u043E\u043D\u0438 \u043F\u0435\u0440\u0435\u0447\u0438\u0441\u043B\u0435\u043D\u044B \u0432 \u0442\u0432\u043E\u0451\u043C \u043F\u0440\u043E\u043C\u043F\u0442\u0435). apply=true \u2014 \u043F\u0440\u0430\u0432\u0438\u043B\u043E \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u0435, apply=false \u2014 \u043E\u0442\u043F\u0438\u0441\u043A\u0430, \u0441\u043D\u044F\u0442\u044C. verify/conventions/brief \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0442\u0441\u044F \u0432 \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0443 \u0441\u0440\u0430\u0437\u0443, env/zones/spine \u0443\u0439\u0434\u0443\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u0432 \u043B\u044E\u0431\u043E\u043C \u0441\u043B\u0443\u0447\u0430\u0435."
11019
11139
  )
11020
11140
  },
11021
- guard(async ({ pr_url, verdict, summary, screenshots, rules, rule_verdicts }) => {
11141
+ guard(async ({ pr_url, verdict, summary, screenshots, rules, rule_verdicts, zone_notes, zone_notes_used }) => {
11022
11142
  const run = await store.myRun();
11023
11143
  let repoFullNameCache = null;
11024
11144
  const repoFullNameOnce = () => repoFullNameCache ??= remoteRepoFullName(opts.worktree ?? process.cwd()).catch(() => null);
@@ -11051,6 +11171,46 @@ ${notFoundText}` : notFoundText }],
11051
11171
  return `
11052
11172
 
11053
11173
  \u{1F4CC} ${line}`;
11174
+ };
11175
+ const zoneMemoryNote = async (taskSlug) => {
11176
+ const used = collectZoneNoteConfirmations({ used: zone_notes_used });
11177
+ if (!zone_notes?.length && used.length === 0) return "";
11178
+ const zone = normalizeZoneLabel(run?.area);
11179
+ const repoFullName = await repoFullNameOnce();
11180
+ if (!repoFullName) {
11181
+ await store.addEvent(null, "step", "\u0437\u0430\u043C\u0435\u0442\u043A\u0438 \u043E \u0437\u043E\u043D\u0435 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u044B: \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0438\u0442\u044C owner/repo \u043F\u043E origin");
11182
+ return "\n\n\u26A0\uFE0F \u0417\u0430\u043C\u0435\u0442\u043A\u0438 \u043E \u0437\u043E\u043D\u0435 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u044B: \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0438\u0442\u044C owner/repo.";
11183
+ }
11184
+ const lines = [];
11185
+ if (zone_notes?.length) {
11186
+ if (!opts.taskless?.proposeZoneNotes) lines.push("\u0437\u0430\u043C\u0435\u0442\u043A\u0430 \u043E \u0437\u043E\u043D\u0435 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0430: Taskless \u043D\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D");
11187
+ else if (!taskSlug) lines.push("\u0437\u0430\u043C\u0435\u0442\u043A\u0430 \u043E \u0437\u043E\u043D\u0435 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0430: \u0441\u043B\u0430\u0433 \u0437\u0430\u0434\u0430\u0447\u0438 \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043D");
11188
+ else {
11189
+ const addressed = zone_notes.map((n) => ({ zone: normalizeZoneLabel(n.zone) || zone, claim: n.claim, why: n.why })).filter((n) => n.zone);
11190
+ if (addressed.length < zone_notes.length) lines.push("\u0437\u0430\u043C\u0435\u0442\u043A\u0430 \u0431\u0435\u0437 \u0437\u043E\u043D\u044B \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0430: \u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435\u0442 area, \u0430 \u0432 \u0441\u0430\u043C\u043E\u0439 \u0437\u0430\u043C\u0435\u0442\u043A\u0435 \u0437\u043E\u043D\u0430 \u043D\u0435 \u043D\u0430\u0437\u0432\u0430\u043D\u0430");
11191
+ if (addressed.length > 0) {
11192
+ const text = await opts.taskless.proposeZoneNotes({
11193
+ repoFullName,
11194
+ notes: addressed,
11195
+ taskSlug,
11196
+ ...run?.name ? { by: run.name } : {},
11197
+ prUrl: pr_url
11198
+ }).catch((e) => `\u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0430: ${e instanceof Error ? e.message : String(e)}`);
11199
+ lines.push(text ?? "\u0437\u0430\u043C\u0435\u0442\u043A\u0430 \u043E \u0437\u043E\u043D\u0435 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0430 (\u043F\u0440\u043E\u0434 \u043E\u0442\u043A\u0430\u0437\u0430\u043B \u0438\u043B\u0438 \u0442\u0443\u043B \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D)");
11200
+ }
11201
+ }
11202
+ }
11203
+ if (used.length > 0) {
11204
+ if (!opts.taskless?.confirmZoneNotes) lines.push("\u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u0437\u0430\u043C\u0435\u0442\u043E\u043A \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E: Taskless \u043D\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D");
11205
+ else {
11206
+ const text = await opts.taskless.confirmZoneNotes({ repoFullName, notes: used, ...zone ? { zone } : {}, ...run?.name ? { by: run.name } : {} }).catch((e) => `\u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E: ${e instanceof Error ? e.message : String(e)}`);
11207
+ lines.push(text ?? "\u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u0437\u0430\u043C\u0435\u0442\u043E\u043A \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E (\u043F\u0440\u043E\u0434 \u043E\u0442\u043A\u0430\u0437\u0430\u043B \u0438\u043B\u0438 \u0442\u0443\u043B \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D)");
11208
+ }
11209
+ }
11210
+ for (const line of lines) await store.addEvent(null, "step", `\u043F\u0430\u043C\u044F\u0442\u044C \u0437\u043E\u043D\u044B: ${line}`);
11211
+ return lines.length ? `
11212
+
11213
+ \u{1F9E0} ${lines.join("; ")}` : "";
11054
11214
  };
11055
11215
  const reportSlug = (slug) => opts.expectedTaskSlug ?? slug;
11056
11216
  const uploadShots = async () => {
@@ -11252,15 +11412,16 @@ ${report}${shots.warning}`,
11252
11412
  await store.addEvent(null, "pr_opened", pr_url);
11253
11413
  await postReport(reportSlug(run.task_slug), t.cycle ?? run.review_cycle ?? 0);
11254
11414
  const rulesNote = await offerRules(reportSlug(run.task_slug));
11415
+ const zoneNote = await zoneMemoryNote(reportSlug(run.task_slug));
11255
11416
  if (t.isFixIteration) {
11256
11417
  await store.addEvent(
11257
11418
  null,
11258
11419
  "step",
11259
11420
  `\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F: \u043D\u043E\u0432\u044B\u0435 \u043A\u043E\u043C\u043C\u0438\u0442\u044B \u043F\u043E\u0441\u043B\u0435 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \xAB\u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438\xBB \u2014 \u043E\u0442\u043A\u0440\u044B\u0442 \u0446\u0438\u043A\u043B \u0440\u0435-\u0440\u0435\u0432\u044C\u044E #${t.cycle}`
11260
11421
  );
11261
- return { content: [{ type: "text", text: `\u043F\u0440\u0438\u043D\u044F\u0442\u043E, \u0443\u0448\u043B\u043E \u043D\u0430 \u0440\u0435-\u0440\u0435\u0432\u044C\u044E (\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F)${shotsNote(shots)}${rulesNote}` }] };
11422
+ return { content: [{ type: "text", text: `\u043F\u0440\u0438\u043D\u044F\u0442\u043E, \u0443\u0448\u043B\u043E \u043D\u0430 \u0440\u0435-\u0440\u0435\u0432\u044C\u044E (\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F)${shotsNote(shots)}${rulesNote}${zoneNote}` }] };
11262
11423
  }
11263
- return { content: [{ type: "text", text: `\u043F\u0440\u0438\u043D\u044F\u0442\u043E, \u0443\u0448\u043B\u043E \u043D\u0430 \u0440\u0435\u0432\u044C\u044E${shotsNote(shots)}${rulesNote}` }] };
11424
+ return { content: [{ type: "text", text: `\u043F\u0440\u0438\u043D\u044F\u0442\u043E, \u0443\u0448\u043B\u043E \u043D\u0430 \u0440\u0435\u0432\u044C\u044E${shotsNote(shots)}${rulesNote}${zoneNote}` }] };
11264
11425
  }
11265
11426
  await store.updateRun(null, { status: "review", pr_url, ...branchFix?.patch ?? {} });
11266
11427
  await store.addEvent(null, "pr_opened", pr_url);
@@ -11268,7 +11429,8 @@ ${report}${shots.warning}`,
11268
11429
  if (fallbackSlug) await postReport(fallbackSlug, void 0);
11269
11430
  else await store.addEvent(null, "step", `\u043E\u0442\u0447\u0451\u0442 \u0434\u0440\u043E\u043D\u0430 (\u0437\u0430\u0434\u0430\u0447\u0430 \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430 \u2014 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435\u0442 \u0432 \u0441\u0442\u043E\u0440\u0435): ${report}`);
11270
11431
  const fallbackRulesNote = await offerRules(fallbackSlug);
11271
- return { content: [{ type: "text", text: `\u043F\u0440\u0438\u043D\u044F\u0442\u043E, \u0443\u0448\u043B\u043E \u043D\u0430 \u0440\u0435\u0432\u044C\u044E${shotsNote(shots)}${fallbackRulesNote}` }] };
11432
+ const fallbackZoneNote = await zoneMemoryNote(fallbackSlug);
11433
+ return { content: [{ type: "text", text: `\u043F\u0440\u0438\u043D\u044F\u0442\u043E, \u0443\u0448\u043B\u043E \u043D\u0430 \u0440\u0435\u0432\u044C\u044E${shotsNote(shots)}${fallbackRulesNote}${fallbackZoneNote}` }] };
11272
11434
  }
11273
11435
  await store.updateRun(null, { status: "done" });
11274
11436
  await store.addEvent(null, "done", pr_url ? `\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043E \u0431\u0435\u0437 GitHub-PR (${pr_url})` : "\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043E \u0431\u0435\u0437 GitHub-PR");
@@ -12828,7 +12990,7 @@ async function changedPaths(worktree, opts = {}) {
12828
12990
  // package.json
12829
12991
  var package_default = {
12830
12992
  name: "@taskless-app/swarm",
12831
- version: "0.6.16",
12993
+ version: "0.6.18",
12832
12994
  type: "module",
12833
12995
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
12834
12996
  keywords: [
@@ -12878,7 +13040,7 @@ var package_default = {
12878
13040
  execa: "^10.0.0",
12879
13041
  zod: "3.25.76"
12880
13042
  },
12881
- gitHead: "785b1478f2b81406c9b6524913939707b6c096fb"
13043
+ gitHead: "9531f684d8a7c6c4c3595f0c57e4aebce0038ac1"
12882
13044
  };
12883
13045
 
12884
13046
  // src/version.ts
@@ -15812,7 +15974,7 @@ ${priorReview}` : `\u0417\u0430\u043C\u0435\u0447\u0430\u043D\u0438\u044F \u2014
15812
15974
  }
15813
15975
  var HERO_CONTINUE_NUDGE = "\u0422\u0435\u0431\u044F \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u043B\u0438, \u0413\u0435\u0440\u043E\u0439: \u0432 \u043E\u0442\u0440\u044F\u0434\u0435 \u0447\u0442\u043E-\u0442\u043E \u043F\u0440\u043E\u0438\u0437\u043E\u0448\u043B\u043E (\u0434\u0440\u043E\u043D \u0437\u0430\u0434\u0430\u043B \u0432\u043E\u043F\u0440\u043E\u0441, \u0441\u0434\u0430\u043B PR \u0438\u043B\u0438 \u0443\u043C\u0435\u0440) \u2014 \u043B\u0438\u0431\u043E \u0442\u0435\u0431\u044F \u043F\u043E\u0437\u0432\u0430\u043B \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440. \u0422\u044B headless, \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0435\u0448\u044C \u0422\u041E\u0422 \u0416\u0415 \u044D\u043F\u0438\u043A: \u0443\u0441\u0442\u0430\u0432 \u0438 \u0442\u0435\u043B\u043E \u044D\u043F\u0438\u043A\u0430 \u0443\u0436\u0435 \u0432 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0435 \u043F\u0440\u043E\u0448\u043B\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0438. \u0413\u043B\u044F\u043D\u044C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u043E\u0442\u0440\u044F\u0434\u0430 (squad_status) \u0438 \u0440\u0430\u0437\u0431\u0435\u0440\u0438\u0441\u044C, \u0447\u0442\u043E \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u043E\u0441\u044C: \u0442\u0435\u0445\u043D\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0434\u0440\u043E\u043D\u043E\u0432 \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0439 answer_drone; \u043F\u0440\u043E\u0434\u0443\u043A\u0442\u043E\u0432\u043E\u0435/\u0434\u0438\u0437\u0430\u0439\u043D/scope \u2014 \u044D\u0441\u043A\u0430\u043B\u0438\u0440\u0443\u0439 \u0447\u0435\u0440\u0435\u0437 ask. \u041A\u043E\u0434 \u043D\u0435 \u043F\u0438\u0448\u0438, PR \u043D\u0435 \u043C\u0435\u0440\u0436\u0438, done \u041D\u0415 \u0437\u043E\u0432\u0438. \u0420\u0430\u0437\u043E\u0431\u0440\u0430\u043B\u0441\u044F \u0441 \u0440\u044B\u0432\u043A\u043E\u043C \u2014 \u0441\u043D\u043E\u0432\u0430 \u0437\u043E\u0432\u0438 rest(summary) \u0438 \u0437\u0430\u043C\u0440\u0438. \u0412\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u0432 stdout \u043D\u0435 \u0437\u0430\u0434\u0430\u0432\u0430\u0439 \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0435\u0440\u0435\u0437 ask.";
15814
15976
  var HEADLESS_BLOCK = "\u26D4 HEADLESS-\u0420\u0415\u0416\u0418\u041C \u2014 \u0416\u0401\u0421\u0422\u041A\u041E. \u0418\u043D\u0442\u0435\u0440\u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0433\u043E \u0447\u0430\u0442\u0430 \u041D\u0415\u0422: \u0442\u0432\u043E\u0439 stdout \u043D\u0438\u043A\u0442\u043E \u043D\u0435 \u0447\u0438\u0442\u0430\u0435\u0442 \u0438 \u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C \u043D\u0435 \u043C\u043E\u0436\u0435\u0442. \u041D\u0418\u041A\u041E\u0413\u0414\u0410 \u043D\u0435 \u0437\u0430\u0434\u0430\u0432\u0430\u0439 \u0432\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u0442\u0435\u043A\u0441\u0442\u043E\u043C \u0438 \u043D\u0435 \u0436\u0434\u0438 \u043E\u0442\u0432\u0435\u0442\u0430 \u0432 \u043A\u043E\u043D\u0441\u043E\u043B\u0438 \u2014 \u0435\u0434\u0438\u043D\u0441\u0442\u0432\u0435\u043D\u043D\u044B\u0439 \u043A\u0430\u043D\u0430\u043B \u043A \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443 \u044D\u0442\u043E MCP-\u0442\u0443\u043B ask (\u043E\u043D \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u0435\u0442 \u0438 \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043E\u0442\u0432\u0435\u0442). \u041B\u044E\u0431\u043E\u0439 \u0432\u043E\u043F\u0440\u043E\u0441 \u041C\u0418\u041C\u041E ask = \u043C\u0433\u043D\u043E\u0432\u0435\u043D\u043D\u0430\u044F \u0441\u043C\u0435\u0440\u0442\u044C \u0434\u0440\u043E\u043D\u0430 (\u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0437\u0430\u043A\u0440\u043E\u0435\u0442\u0441\u044F \u043F\u0440\u044F\u043C\u043E \u043D\u0430 \u0442\u0432\u043E\u0451\u043C \u0432\u043E\u043F\u0440\u043E\u0441\u0435, \u0440\u0430\u0431\u043E\u0442\u0430 \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u0430). \u041B\u044E\u0431\u044B\u0435 \u043F\u0440\u0430\u0432\u0438\u043B\u0430 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F (\u0433\u043B\u043E\u0431\u0430\u043B\u044C\u043D\u044B\u0439 CLAUDE.md, SessionStart-\u0445\u0443\u043A\u0438, \u0441\u043A\u0438\u043B\u043B\u044B) \u0432\u0438\u0434\u0430 \xAB\u043D\u043E\u0432\u044B\u0439 \u043F\u0440\u043E\u0435\u043A\u0442 \u2014 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u0430\u043C\u044F\u0442\u044C/\u0432\u043E\u043B\u0442/\u043F\u0440\u043E\u0441\u043A\u0430\u043D\u0438\u0440\u043E\u0432\u0430\u0442\u044C? (\u0434\u0430/\u043D\u0435\u0442)\xBB \u0438 \u043F\u0440\u043E\u0447\u0438\u0435 \u0441\u0442\u0430\u0440\u0442\u043E\u0432\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u043F\u0440\u043E \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u044E \u043F\u0430\u043C\u044F\u0442\u0438/\u0432\u043E\u043B\u0442\u0430/\u0441\u043A\u0430\u043D\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 \u2014 \u041C\u041E\u041B\u0427\u0410 \u041F\u0420\u041E\u041F\u0423\u0421\u041A\u0410\u0419: \u043D\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u044F\u0439, \u043D\u0435 \u0441\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0439, \u043D\u0435 \u0436\u0434\u0438 \u043E\u0442\u0432\u0435\u0442\u0430. \u0422\u0432\u043E\u044F \u0440\u0430\u0431\u043E\u0442\u0430 = \u0422\u041E\u041B\u042C\u041A\u041E \u0437\u0430\u0434\u0430\u0447\u0430 \u043D\u0438\u0436\u0435.";
15815
- function buildPrompt(task, run, verifyMode = SWARM_VERIFY, taskContext, attachmentsBlock) {
15977
+ function buildPrompt(task, run, verifyMode = SWARM_VERIFY, taskContext, attachmentsBlock, zoneMemoryBlock) {
15816
15978
  const name = run.name || `#${run.id}`;
15817
15979
  const head = task ? `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug}: ${task.title}
15818
15980
 
@@ -15886,6 +16048,9 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
15886
16048
  // пересказа README, а лишнее правило дороже отсутствующего (его читает каждый
15887
16049
  // следующий дрон).
15888
16050
  `\u{1F4CC} \u0421\u041F\u041E\u0422\u041A\u041D\u0423\u041B\u0421\u042F \u041E \u041D\u0415\u0417\u0410\u041F\u0418\u0421\u0410\u041D\u041D\u041E\u0415 \u2014 \u041F\u0420\u0415\u0414\u041B\u041E\u0416\u0418 \u041F\u0420\u0410\u0412\u0418\u041B\u041E. \u041F\u043E\u0442\u0440\u0430\u0442\u0438\u043B \u0432\u0440\u0435\u043C\u044F \u043D\u0430 \u0442\u043E, \u0447\u0435\u0433\u043E \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0438\u0433\u0434\u0435 \u043D\u0435 \u043D\u0430\u043F\u0438\u0441\u0430\u043D\u043E (\u0438\u043D\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043E\u043D\u043D\u044B\u0435 \u0442\u0435\u0441\u0442\u044B \u0436\u0434\u0443\u0442 \u043F\u043E\u0441\u0442\u0433\u0440\u0435\u0441 \u043D\u0430 5433; \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043D\u0430\u043A\u0430\u0442\u044B\u0432\u0430\u044E\u0442\u0441\u044F \u043D\u0435 \u0442\u0435\u043C, \u0447\u0435\u043C \u043A\u0430\u0436\u0435\u0442\u0441\u044F; \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0437\u0435\u043B\u0451\u043D\u0430\u044F \u0442\u043E\u043B\u044C\u043A\u043E \u0441 \u0442\u0430\u043A\u0438\u043C \u0444\u043B\u0430\u0433\u043E\u043C) \u2014 \u043F\u0435\u0440\u0435\u0434\u0430\u0439 \u044D\u0442\u043E \u0432 done: \`rules: [{ field: "verify"|"conventions"|"brief"|"env"|"zones"|"spine", value: <\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043F\u043E\u043B\u044F \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0438>, why: "\u043E\u0431\u043E \u0447\u0442\u043E \u0441\u043F\u043E\u0442\u043A\u043D\u0443\u043B\u0441\u044F \u0438 \u0447\u0435\u0433\u043E \u044D\u0442\u043E \u0441\u0442\u043E\u0438\u043B\u043E" }]\`. \u041D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 ${DRONE_RULES_PER_RUN_MAX} \u0437\u0430 \u043F\u0440\u043E\u0433\u043E\u043D \u0438 \u0422\u041E\u041B\u042C\u041A\u041E \u0442\u043E, \u043E\u0431\u043E \u0447\u0442\u043E \u0440\u0435\u0430\u043B\u044C\u043D\u043E \u0441\u043F\u043E\u0442\u043A\u043D\u0443\u043B\u0441\u044F: \xAB\u0442\u0435\u0441\u0442\u044B \u0433\u043E\u043D\u044F\u044E\u0442\u0441\u044F \u043E\u0434\u043D\u043E\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439, \u043E\u043D\u0430 \u0432 README\xBB \u2014 \u043D\u0435 \u043F\u0440\u0430\u0432\u0438\u043B\u043E, \u0430 \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0437 README. \u0423\u0436\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043D\u043E\u0435 \u043E\u0442\u0441\u0435\u0435\u0442\u0441\u044F \u0441\u0430\u043C\u043E; \u0440\u0435\u0448\u0438\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u043E\u043C. \u041D\u0438\u0447\u0435\u0433\u043E \u0442\u0430\u043A\u043E\u0433\u043E \u043D\u0435 \u0431\u044B\u043B\u043E \u2014 \u043D\u0435 \u0432\u044B\u0434\u0443\u043C\u044B\u0432\u0430\u0439, \u043F\u0443\u0441\u0442\u043E\u0439 \u0441\u043F\u0438\u0441\u043E\u043A \u044D\u0442\u043E \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0438\u0441\u0445\u043E\u0434 \u043F\u0440\u043E\u0433\u043E\u043D\u0430.`,
16051
+ // TASWM-466: рядом с правилом о репозитории — у них один смысл (знание, добытое
16052
+ // прогоном, переживает прогон) и одна цена ошибки: лишняя заметка дороже отсутствующей.
16053
+ ...zoneMemoryBlock ? [zoneMemoryBlock] : [],
15889
16054
  "\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."
15890
16055
  ];
15891
16056
  if (run.decision) {
@@ -18157,10 +18322,13 @@ async function respawnRun(deps, run, mode, nudge) {
18157
18322
  deps.db.updateRun(run.id, { status: "running", pid: pid2 });
18158
18323
  return;
18159
18324
  }
18160
- const [task, verifyMode, projectBase] = await Promise.all([
18325
+ const [task, verifyMode, projectBase, zoneNotes] = await Promise.all([
18161
18326
  deps.getTask(run.task_slug, run.workspace_id),
18162
18327
  deps.verifyMode ? deps.verifyMode(repo) : Promise.resolve(void 0),
18163
- deps.baseBranch ? deps.baseBranch(repo) : Promise.resolve("")
18328
+ deps.baseBranch ? deps.baseBranch(repo) : Promise.resolve(""),
18329
+ // TASWM-466: память зоны — тем же параллельным чтением. Отвалилась → пустой список:
18330
+ // оживление дрона не должно зависеть от того, доехала ли подсказка.
18331
+ deps.zoneMemory && run.area ? deps.zoneMemory(repo, run.area).catch(() => []) : Promise.resolve([])
18164
18332
  ]);
18165
18333
  const base3 = effectiveBaseBranch(run.base_branch, projectBase);
18166
18334
  const runToSpawn = base3 && run.base_branch !== base3 ? deps.db.updateRun(run.id, { base_branch: base3 }) ?? run : run;
@@ -18184,7 +18352,7 @@ async function respawnRun(deps, run, mode, nudge) {
18184
18352
  fetchFile: deps.fetchFile,
18185
18353
  onEvent: (text) => deps.db.addEvent(run.id, "step", text)
18186
18354
  });
18187
- const freshPrompt = runToSpawn.kind === "hero" ? buildHeroPrompt(task, runToSpawn, taskContext, attachments) : buildPrompt(task, runToSpawn, verifyMode, taskContext, attachments);
18355
+ const freshPrompt = runToSpawn.kind === "hero" ? buildHeroPrompt(task, runToSpawn, taskContext, attachments) : buildPrompt(task, runToSpawn, verifyMode, taskContext, attachments, renderZoneMemoryBlock(runToSpawn.area, zoneNotes));
18188
18356
  const notice = depsPromptNotice(depsReport);
18189
18357
  const prompt = [freshPrompt, notice, nudge].filter(Boolean).join("\n\n");
18190
18358
  const pid = deps.launch({ worktree: wt, run: runToSpawn, prompt });
@@ -18653,12 +18821,17 @@ async function runDaemon(opts) {
18653
18821
  let projectDroneCap;
18654
18822
  let autoReviewProject;
18655
18823
  let pendingRepoRules;
18824
+ let zoneMemoryReader;
18656
18825
  if (SWARM_BACKEND === "client" && db instanceof SwarmClient) {
18657
18826
  const client = db;
18658
18827
  pendingRepoRules = async (repo, taskSlug) => {
18659
18828
  const repoFullName = await remoteRepoFullName(repo).catch(() => null);
18660
18829
  return repoFullName ? client.repoPendingRules(repoFullName, taskSlug) : [];
18661
18830
  };
18831
+ zoneMemoryReader = async (repo, zone) => {
18832
+ const repoFullName = await remoteRepoFullName(repo).catch(() => null);
18833
+ return repoFullName ? parseZoneMemory(await client.repoZoneMemory(repoFullName), zone) : [];
18834
+ };
18662
18835
  verifyModeReader = makeVerifyModeReader({
18663
18836
  fetch: async (repo) => {
18664
18837
  const repoFullName = await remoteRepoFullName(repo).catch(() => null);
@@ -18823,10 +18996,13 @@ async function runDaemon(opts) {
18823
18996
  spawn: async (run) => {
18824
18997
  const repoOfRun = normalizeRepo(run.repo || opts.repo);
18825
18998
  const taskClient = tlFor(run.workspace_id);
18826
- const [rawTask, verifyMode, projectBase] = await Promise.all([
18999
+ const [rawTask, verifyMode, projectBase, zoneNotes] = await Promise.all([
18827
19000
  taskClient ? taskClient.getTask(run.task_slug) : Promise.resolve(null),
18828
19001
  verifyModeReader ? verifyModeReader(repoOfRun) : Promise.resolve(void 0),
18829
- baseBranchReader ? baseBranchReader(repoOfRun) : Promise.resolve("")
19002
+ baseBranchReader ? baseBranchReader(repoOfRun) : Promise.resolve(""),
19003
+ // TASWM-466: память зоны прогона — тем же параллельным чтением, что и остальные
19004
+ // живые ручки. Зоны у прогона нет (герой, разметка без area) → читать нечего.
19005
+ zoneMemoryReader && run.area ? zoneMemoryReader(repoOfRun, run.area).catch(() => []) : Promise.resolve([])
18830
19006
  ]);
18831
19007
  const task = taskClient ? requireTaskBody(rawTask, run.task_slug, run.workspace_id) : null;
18832
19008
  const healed = healTaskSlug(db, run, task);
@@ -18843,7 +19019,7 @@ async function runDaemon(opts) {
18843
19019
  fetchFile: taskClient?.fetchFile?.bind(taskClient),
18844
19020
  onEvent: (text) => db.addEvent(r.id, "step", text)
18845
19021
  });
18846
- return r.kind === "hero" ? buildHeroPrompt(task, r, taskContext, attachments) : buildPrompt(task, r, verifyMode, taskContext, attachments);
19022
+ return r.kind === "hero" ? buildHeroPrompt(task, r, taskContext, attachments) : buildPrompt(task, r, verifyMode, taskContext, attachments, renderZoneMemoryBlock(r.area, zoneNotes));
18847
19023
  }
18848
19024
  },
18849
19025
  runToSpawn
@@ -19055,6 +19231,9 @@ async function runDaemon(opts) {
19055
19231
  return client ? requireTaskBody(await client.getTask(slug), slug, ws) : null;
19056
19232
  },
19057
19233
  ...verifyModeReader ? { verifyMode: verifyModeReader } : {},
19234
+ // TASWM-466: тот же ридер памяти зоны, что на спавне — оживлённый дрон строит
19235
+ // промпт с нуля и без него потерял бы память своей зоны.
19236
+ ...zoneMemoryReader ? { zoneMemory: zoneMemoryReader } : {},
19058
19237
  getTaskContext: (task, ws) => {
19059
19238
  const client = tlFor(ws);
19060
19239
  return client ? fetchTaskContext(client, task) : Promise.resolve(void 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taskless-app/swarm",
3
- "version": "0.6.16",
3
+ "version": "0.6.18",
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": "785b1478f2b81406c9b6524913939707b6c096fb",
41
+ "gitHead": "9531f684d8a7c6c4c3595f0c57e4aebce0038ac1",
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",