@taskless-app/swarm 0.6.42 → 0.6.44
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 +11 -2
- package/dist/overmind.js +47 -9
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -3390,7 +3390,7 @@ var init_package = __esm({
|
|
|
3390
3390
|
"package.json"() {
|
|
3391
3391
|
package_default = {
|
|
3392
3392
|
name: "@taskless-app/swarm",
|
|
3393
|
-
version: "0.6.
|
|
3393
|
+
version: "0.6.44",
|
|
3394
3394
|
type: "module",
|
|
3395
3395
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
3396
3396
|
keywords: [
|
|
@@ -3441,7 +3441,7 @@ var init_package = __esm({
|
|
|
3441
3441
|
execa: "^10.0.0",
|
|
3442
3442
|
zod: "3.25.76"
|
|
3443
3443
|
},
|
|
3444
|
-
gitHead: "
|
|
3444
|
+
gitHead: "21322aad3f4de6898a9aa9cc2eb884cfdd2ebb5e"
|
|
3445
3445
|
};
|
|
3446
3446
|
}
|
|
3447
3447
|
});
|
|
@@ -4778,6 +4778,9 @@ function rowToRun(row) {
|
|
|
4778
4778
|
// старые прогоны без колонки → 0
|
|
4779
4779
|
task_title: row.task_title ?? null,
|
|
4780
4780
|
task_desc: row.task_desc ?? null,
|
|
4781
|
+
// TASWM-556: когда заведена сама задача — по ней проверка дубля отличает свой
|
|
4782
|
+
// смерженный PR от чужого на переиспользованном слаге.
|
|
4783
|
+
task_created_at: row.task_created_at ?? null,
|
|
4781
4784
|
task_url: row.task_url ?? null,
|
|
4782
4785
|
priority: row.priority ?? 0,
|
|
4783
4786
|
// старые прогоны без колонки → 0 (обычный приоритет)
|
|
@@ -4849,6 +4852,7 @@ CREATE TABLE IF NOT EXISTS runs (
|
|
|
4849
4852
|
review_cycle INTEGER NOT NULL DEFAULT 0,
|
|
4850
4853
|
task_title TEXT,
|
|
4851
4854
|
task_desc TEXT,
|
|
4855
|
+
task_created_at TEXT,
|
|
4852
4856
|
task_url TEXT,
|
|
4853
4857
|
priority INTEGER NOT NULL DEFAULT 0,
|
|
4854
4858
|
base_branch TEXT,
|
|
@@ -4921,6 +4925,8 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
4921
4925
|
"ALTER TABLE runs ADD COLUMN depends_on TEXT",
|
|
4922
4926
|
"ALTER TABLE runs ADD COLUMN task_title TEXT",
|
|
4923
4927
|
"ALTER TABLE runs ADD COLUMN task_desc TEXT",
|
|
4928
|
+
// TASWM-556: время создания задачи (снимок из прода) — признак «этот смерженный PR мой».
|
|
4929
|
+
"ALTER TABLE runs ADD COLUMN task_created_at TEXT",
|
|
4924
4930
|
"ALTER TABLE runs ADD COLUMN task_url TEXT",
|
|
4925
4931
|
// Иерархия роя (TASWM-78): роль прогона (drone|hero) и ссылка на прогон-героя.
|
|
4926
4932
|
"ALTER TABLE runs ADD COLUMN kind TEXT NOT NULL DEFAULT 'drone'",
|
|
@@ -9276,6 +9282,8 @@ var SwarmClient = class _SwarmClient {
|
|
|
9276
9282
|
task_title: null,
|
|
9277
9283
|
task_desc: null,
|
|
9278
9284
|
task_url: null,
|
|
9285
|
+
// TASWM-556: когда заведена задача — прод-поле снимка, заполняется ниже из ответа.
|
|
9286
|
+
task_created_at: null,
|
|
9279
9287
|
// Машина прогона (TASWM-281) и её закрепление (TASWM-291) — прод-поля,
|
|
9280
9288
|
// заполняются ниже из ответа.
|
|
9281
9289
|
host_id: null,
|
|
@@ -9299,6 +9307,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
9299
9307
|
else if (!isTaskSlug(local.task_slug)) local.task_slug = pr.taskId || local.task_slug;
|
|
9300
9308
|
local.task_title = pr.taskTitle ?? local.task_title;
|
|
9301
9309
|
local.task_desc = pr.taskDesc ?? local.task_desc;
|
|
9310
|
+
local.task_created_at = pr.taskCreatedAt ?? local.task_created_at;
|
|
9302
9311
|
local.name = pr.droneName ?? local.name;
|
|
9303
9312
|
local.kind = pr.role ?? local.kind;
|
|
9304
9313
|
const heroProd = pr.heroId ?? null;
|
package/dist/overmind.js
CHANGED
|
@@ -1896,6 +1896,17 @@ function prBelongsToTask(slug, pr) {
|
|
|
1896
1896
|
const after = title[at + s.length] ?? "";
|
|
1897
1897
|
return !/[a-z0-9]/.test(before) && !/[a-z0-9-]/.test(after);
|
|
1898
1898
|
}
|
|
1899
|
+
function mergedPrIsTaskWork(slug, pr, taskCreatedAt) {
|
|
1900
|
+
if (!prBelongsToTask(slug, pr)) return false;
|
|
1901
|
+
const merged = Date.parse((pr.mergedAt ?? "").trim());
|
|
1902
|
+
const created = Date.parse((taskCreatedAt ?? "").trim());
|
|
1903
|
+
if (!Number.isFinite(merged) || !Number.isFinite(created)) return true;
|
|
1904
|
+
return merged >= created;
|
|
1905
|
+
}
|
|
1906
|
+
function pickTaskMergedPr(slug, prs, taskCreatedAt) {
|
|
1907
|
+
const own = prs.filter((p) => typeof p.url === "string" && p.url.trim() && mergedPrIsTaskWork(slug, p, taskCreatedAt)).sort((a, b) => (Date.parse((b.mergedAt ?? "").trim()) || 0) - (Date.parse((a.mergedAt ?? "").trim()) || 0));
|
|
1908
|
+
return own[0]?.url?.trim() || null;
|
|
1909
|
+
}
|
|
1899
1910
|
function decideTaskWork(facts) {
|
|
1900
1911
|
const candidate = (facts.mergedCandidate ?? "").trim() || null;
|
|
1901
1912
|
const open = [];
|
|
@@ -6203,6 +6214,9 @@ function rowToRun(row) {
|
|
|
6203
6214
|
// старые прогоны без колонки → 0
|
|
6204
6215
|
task_title: row.task_title ?? null,
|
|
6205
6216
|
task_desc: row.task_desc ?? null,
|
|
6217
|
+
// TASWM-556: когда заведена сама задача — по ней проверка дубля отличает свой
|
|
6218
|
+
// смерженный PR от чужого на переиспользованном слаге.
|
|
6219
|
+
task_created_at: row.task_created_at ?? null,
|
|
6206
6220
|
task_url: row.task_url ?? null,
|
|
6207
6221
|
priority: row.priority ?? 0,
|
|
6208
6222
|
// старые прогоны без колонки → 0 (обычный приоритет)
|
|
@@ -6274,6 +6288,7 @@ CREATE TABLE IF NOT EXISTS runs (
|
|
|
6274
6288
|
review_cycle INTEGER NOT NULL DEFAULT 0,
|
|
6275
6289
|
task_title TEXT,
|
|
6276
6290
|
task_desc TEXT,
|
|
6291
|
+
task_created_at TEXT,
|
|
6277
6292
|
task_url TEXT,
|
|
6278
6293
|
priority INTEGER NOT NULL DEFAULT 0,
|
|
6279
6294
|
base_branch TEXT,
|
|
@@ -6346,6 +6361,8 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
6346
6361
|
"ALTER TABLE runs ADD COLUMN depends_on TEXT",
|
|
6347
6362
|
"ALTER TABLE runs ADD COLUMN task_title TEXT",
|
|
6348
6363
|
"ALTER TABLE runs ADD COLUMN task_desc TEXT",
|
|
6364
|
+
// TASWM-556: время создания задачи (снимок из прода) — признак «этот смерженный PR мой».
|
|
6365
|
+
"ALTER TABLE runs ADD COLUMN task_created_at TEXT",
|
|
6349
6366
|
"ALTER TABLE runs ADD COLUMN task_url TEXT",
|
|
6350
6367
|
// Иерархия роя (TASWM-78): роль прогона (drone|hero) и ссылка на прогон-героя.
|
|
6351
6368
|
"ALTER TABLE runs ADD COLUMN kind TEXT NOT NULL DEFAULT 'drone'",
|
|
@@ -9503,6 +9520,8 @@ var SwarmClient = class _SwarmClient {
|
|
|
9503
9520
|
task_title: null,
|
|
9504
9521
|
task_desc: null,
|
|
9505
9522
|
task_url: null,
|
|
9523
|
+
// TASWM-556: когда заведена задача — прод-поле снимка, заполняется ниже из ответа.
|
|
9524
|
+
task_created_at: null,
|
|
9506
9525
|
// Машина прогона (TASWM-281) и её закрепление (TASWM-291) — прод-поля,
|
|
9507
9526
|
// заполняются ниже из ответа.
|
|
9508
9527
|
host_id: null,
|
|
@@ -9526,6 +9545,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
9526
9545
|
else if (!isTaskSlug(local.task_slug)) local.task_slug = pr.taskId || local.task_slug;
|
|
9527
9546
|
local.task_title = pr.taskTitle ?? local.task_title;
|
|
9528
9547
|
local.task_desc = pr.taskDesc ?? local.task_desc;
|
|
9548
|
+
local.task_created_at = pr.taskCreatedAt ?? local.task_created_at;
|
|
9529
9549
|
local.name = pr.droneName ?? local.name;
|
|
9530
9550
|
local.kind = pr.role ?? local.kind;
|
|
9531
9551
|
const heroProd = pr.heroId ?? null;
|
|
@@ -12294,7 +12314,7 @@ function parkReasonUnretriable(reason) {
|
|
|
12294
12314
|
// package.json
|
|
12295
12315
|
var package_default = {
|
|
12296
12316
|
name: "@taskless-app/swarm",
|
|
12297
|
-
version: "0.6.
|
|
12317
|
+
version: "0.6.44",
|
|
12298
12318
|
type: "module",
|
|
12299
12319
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
12300
12320
|
keywords: [
|
|
@@ -12345,7 +12365,7 @@ var package_default = {
|
|
|
12345
12365
|
execa: "^10.0.0",
|
|
12346
12366
|
zod: "3.25.76"
|
|
12347
12367
|
},
|
|
12348
|
-
gitHead: "
|
|
12368
|
+
gitHead: "21322aad3f4de6898a9aa9cc2eb884cfdd2ebb5e"
|
|
12349
12369
|
};
|
|
12350
12370
|
|
|
12351
12371
|
// src/version.ts
|
|
@@ -12689,13 +12709,17 @@ async function openPrsForBranch(branch, repoDir, run = realRepoRunner) {
|
|
|
12689
12709
|
return [];
|
|
12690
12710
|
}
|
|
12691
12711
|
}
|
|
12692
|
-
async function mergedPrForBranch(branch, repoDir, run = realRepoRunner) {
|
|
12712
|
+
async function mergedPrForBranch(branch, repoDir, run = realRepoRunner, taskCreatedAt) {
|
|
12693
12713
|
if (!branch || !repoDir) return null;
|
|
12694
12714
|
try {
|
|
12695
|
-
const out = await run(
|
|
12715
|
+
const out = await run(
|
|
12716
|
+
["pr", "list", "--head", branch, "--state", "merged", "--json", "url,headRefName,title,mergedAt", "--limit", "20"],
|
|
12717
|
+
repoDir
|
|
12718
|
+
);
|
|
12696
12719
|
const arr = JSON.parse(out);
|
|
12697
|
-
|
|
12698
|
-
|
|
12720
|
+
if (!Array.isArray(arr)) return null;
|
|
12721
|
+
const prs = arr.map((p) => ({ ...p, headRefName: p.headRefName ?? branch }));
|
|
12722
|
+
return pickTaskMergedPr(branch, prs, taskCreatedAt);
|
|
12699
12723
|
} catch {
|
|
12700
12724
|
return null;
|
|
12701
12725
|
}
|
|
@@ -18333,6 +18357,20 @@ async function taskWorkState(deps, slug, repoDir, candidate) {
|
|
|
18333
18357
|
const openPrs = deps.openPrsForTask ? await deps.openPrsForTask(slug, repoDir).catch(() => null) : null;
|
|
18334
18358
|
return decideTaskWork({ mergedCandidate: candidate, openPrs });
|
|
18335
18359
|
}
|
|
18360
|
+
function taskCreatedAtFor(runs) {
|
|
18361
|
+
let best = null;
|
|
18362
|
+
for (const r of runs) {
|
|
18363
|
+
const at = (r.task_created_at ?? "").trim();
|
|
18364
|
+
if (!at) continue;
|
|
18365
|
+
if (!best || r.id > best.id) best = { id: r.id, at };
|
|
18366
|
+
}
|
|
18367
|
+
return best?.at ?? null;
|
|
18368
|
+
}
|
|
18369
|
+
function taskCreatedAtForRun(deps, run) {
|
|
18370
|
+
const own = (run.task_created_at ?? "").trim();
|
|
18371
|
+
if (own) return own;
|
|
18372
|
+
return taskCreatedAtFor(deps.db.listRuns().filter((r) => r.task_slug === run.task_slug));
|
|
18373
|
+
}
|
|
18336
18374
|
function noteOpenPrHold(deps, run, v) {
|
|
18337
18375
|
if (deps.db.events(run.id).some((e) => (e.text ?? "").includes(OPEN_PR_HOLD_MARK))) return;
|
|
18338
18376
|
deps.db.addEvent(run.id, "step", formatOpenPrHold(v));
|
|
@@ -18376,7 +18414,7 @@ async function closeMergedTaskRuns(deps) {
|
|
|
18376
18414
|
}
|
|
18377
18415
|
}
|
|
18378
18416
|
if (!mergedUrl && deps.mergedPrForBranch) {
|
|
18379
|
-
mergedUrl = await deps.mergedPrForBranch(slug, group[0].repo || "").catch(() => null);
|
|
18417
|
+
mergedUrl = await deps.mergedPrForBranch(slug, group[0].repo || "", taskCreatedAtFor(group)).catch(() => null);
|
|
18380
18418
|
}
|
|
18381
18419
|
if (!mergedUrl) continue;
|
|
18382
18420
|
const work = await taskWorkState(deps, slug, group[0].repo || "", mergedUrl);
|
|
@@ -19678,7 +19716,7 @@ async function trySpawnQueuedRun(deps, run) {
|
|
|
19678
19716
|
if (d.log) console.error(`[overmind] ${run.task_slug}: ${why}${d.changed ? "" : ` \u2014 \u0435\u0449\u0451 \u0432\u0438\u0441\u0438\u0442, \u0442\u0438\u043A ${d.ticks}`}`);
|
|
19679
19717
|
return "closed";
|
|
19680
19718
|
}
|
|
19681
|
-
const mergedCandidate = doneMergedPrForSlug(deps.db, run.task_slug) ?? (deps.mergedPrForBranch ? await deps.mergedPrForBranch(run.task_slug, run.repo || "").catch(() => null) : null);
|
|
19719
|
+
const mergedCandidate = doneMergedPrForSlug(deps.db, run.task_slug) ?? (deps.mergedPrForBranch ? await deps.mergedPrForBranch(run.task_slug, run.repo || "", taskCreatedAtForRun(deps, run)).catch(() => null) : null);
|
|
19682
19720
|
const work = mergedCandidate ? await taskWorkState(deps, run.task_slug, run.repo || "", mergedCandidate) : null;
|
|
19683
19721
|
if (work?.kind === "open") noteOpenPrHold(deps, run, work);
|
|
19684
19722
|
const mergedUrl = work?.kind === "merged" ? work.pr : null;
|
|
@@ -22268,7 +22306,7 @@ async function runDaemon(opts) {
|
|
|
22268
22306
|
commitsSince: prHasCommitsSince,
|
|
22269
22307
|
// Смерженный PR ветки-слага (TASWM-206) — тот же фолбэк на дефолт-репо роя, что и
|
|
22270
22308
|
// openPrsForBranch выше; реальный gh, логика закрытия дублей — в tickOnce (тестируется).
|
|
22271
|
-
mergedPrForBranch: (branch, repoDir) => mergedPrForBranch(branch, repoDir || opts.repo),
|
|
22309
|
+
mergedPrForBranch: (branch, repoDir, taskCreatedAt) => mergedPrForBranch(branch, repoDir || opts.repo, void 0, taskCreatedAt),
|
|
22272
22310
|
// ВСЕ открытые PR задачи (TASWM-515) — тот же фолбэк на дефолт-репо роя. Реальный gh;
|
|
22273
22311
|
// и отбор «этот PR — задачи», и решение «закончена ли работа» чистые (swarm-core).
|
|
22274
22312
|
openPrsForTask: (slug, repoDir) => openPrsForTask(slug, repoDir || opts.repo),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taskless-app/swarm",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.44",
|
|
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": "
|
|
42
|
+
"gitHead": "21322aad3f4de6898a9aa9cc2eb884cfdd2ebb5e",
|
|
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",
|