@gethmy/agent 1.7.3 → 1.8.0
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 +191 -84
- package/dist/index.js +191 -84
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1162,6 +1162,7 @@ class MergeMonitor {
|
|
|
1162
1162
|
running = false;
|
|
1163
1163
|
provider;
|
|
1164
1164
|
cwd;
|
|
1165
|
+
onCardCompleted = null;
|
|
1165
1166
|
constructor(client, projectId, config, intervalMs = 60000) {
|
|
1166
1167
|
this.client = client;
|
|
1167
1168
|
this.projectId = projectId;
|
|
@@ -1282,6 +1283,13 @@ class MergeMonitor {
|
|
|
1282
1283
|
log.info(TAG7, `Deleted local branch ${branchName}`);
|
|
1283
1284
|
} catch {}
|
|
1284
1285
|
}
|
|
1286
|
+
if (this.onCardCompleted) {
|
|
1287
|
+
try {
|
|
1288
|
+
await this.onCardCompleted(card);
|
|
1289
|
+
} catch (err) {
|
|
1290
|
+
log.warn(TAG7, `successor promotion failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1285
1293
|
log.info(TAG7, `#${card.short_id} completed (merged)`);
|
|
1286
1294
|
}
|
|
1287
1295
|
}
|
|
@@ -2028,7 +2036,7 @@ function buildTokenPayload(stats) {
|
|
|
2028
2036
|
modelName: stats.cost.modelName
|
|
2029
2037
|
};
|
|
2030
2038
|
}
|
|
2031
|
-
async function runCompletion(client, card, branchName, worktreePath, config, workerId, sessionStats, workspaceId, agentSessionId, stateStore) {
|
|
2039
|
+
async function runCompletion(client, card, branchName, worktreePath, config, workerId, sessionStats, workspaceId, agentSessionId, stateStore, onMovedToCompletion) {
|
|
2032
2040
|
let verificationResult = {
|
|
2033
2041
|
passed: true,
|
|
2034
2042
|
buildErrors: [],
|
|
@@ -2137,6 +2145,13 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
|
|
|
2137
2145
|
}
|
|
2138
2146
|
if (config.completion.moveToColumn) {
|
|
2139
2147
|
await moveCardToColumn(client, card, config.completion.moveToColumn);
|
|
2148
|
+
if (onMovedToCompletion) {
|
|
2149
|
+
try {
|
|
2150
|
+
await onMovedToCompletion(card);
|
|
2151
|
+
} catch (err) {
|
|
2152
|
+
log.warn(TAG12, `successor promotion failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2140
2155
|
}
|
|
2141
2156
|
if (config.completion.postSummary) {
|
|
2142
2157
|
await postSummary(client, card, branchName, worktreePath, prUrl, config.worktree.baseBranch, sessionStats);
|
|
@@ -4248,9 +4263,66 @@ var init_review_worker = __esm(() => {
|
|
|
4248
4263
|
init_worktree();
|
|
4249
4264
|
});
|
|
4250
4265
|
|
|
4266
|
+
// src/unblock.ts
|
|
4267
|
+
async function fetchBlocksLinks(client, cardId) {
|
|
4268
|
+
try {
|
|
4269
|
+
const { links } = await client.getCardLinks(cardId);
|
|
4270
|
+
return links.filter((l) => l.link_type === "blocks");
|
|
4271
|
+
} catch (err) {
|
|
4272
|
+
log.warn(TAG20, `link fetch failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
|
|
4273
|
+
return null;
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4276
|
+
function isBlockerResolved(blocker, columns) {
|
|
4277
|
+
if (blocker.done)
|
|
4278
|
+
return true;
|
|
4279
|
+
const blockerColumn = columns.find((c) => c.id === blocker.column_id);
|
|
4280
|
+
return blockerColumn?.mark_cards_done === true;
|
|
4281
|
+
}
|
|
4282
|
+
async function getUnresolvedBlockers(client, card, projectId) {
|
|
4283
|
+
const links = await fetchBlocksLinks(client, card.id);
|
|
4284
|
+
if (!links)
|
|
4285
|
+
return null;
|
|
4286
|
+
const incoming = links.filter((l) => l.direction === "incoming");
|
|
4287
|
+
if (incoming.length === 0)
|
|
4288
|
+
return [];
|
|
4289
|
+
const board = await client.getBoard(projectId, { summary: true });
|
|
4290
|
+
const columns = board.columns ?? [];
|
|
4291
|
+
return incoming.filter((l) => !isBlockerResolved(l.target_card, columns)).map((l) => ({
|
|
4292
|
+
cardId: l.target_card.id,
|
|
4293
|
+
shortId: l.target_card.short_id,
|
|
4294
|
+
title: l.target_card.title
|
|
4295
|
+
}));
|
|
4296
|
+
}
|
|
4297
|
+
async function promoteUnblockedSuccessors(completedCard, deps) {
|
|
4298
|
+
const links = await fetchBlocksLinks(deps.client, completedCard.id);
|
|
4299
|
+
if (!links)
|
|
4300
|
+
return;
|
|
4301
|
+
const successors = links.filter((l) => l.direction === "outgoing" && !l.target_card.done);
|
|
4302
|
+
if (successors.length === 0)
|
|
4303
|
+
return;
|
|
4304
|
+
log.info(TAG20, `#${completedCard.short_id} completed — checking ${successors.length} chained successor(s)`);
|
|
4305
|
+
for (const link of successors) {
|
|
4306
|
+
const successorId = link.target_card.id;
|
|
4307
|
+
try {
|
|
4308
|
+
const { card } = await deps.client.getCard(successorId);
|
|
4309
|
+
if (card.assignee_id !== deps.agentUserId) {
|
|
4310
|
+
log.debug(TAG20, `successor #${card.short_id} not assigned to agent — skipping promotion`);
|
|
4311
|
+
continue;
|
|
4312
|
+
}
|
|
4313
|
+
await deps.enqueue(successorId);
|
|
4314
|
+
} catch (err) {
|
|
4315
|
+
log.warn(TAG20, `promotion failed for successor ${successorId}: ${err instanceof Error ? err.message : err}`);
|
|
4316
|
+
}
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
var TAG20 = "unblock";
|
|
4320
|
+
var init_unblock = __esm(() => {
|
|
4321
|
+
init_log();
|
|
4322
|
+
});
|
|
4323
|
+
|
|
4251
4324
|
// ../harmony-shared/dist/cardLinks.js
|
|
4252
4325
|
var init_cardLinks = () => {};
|
|
4253
|
-
|
|
4254
4326
|
// ../harmony-shared/dist/commentSerializer.js
|
|
4255
4327
|
function sanitizeHeaderField(value) {
|
|
4256
4328
|
return value.replace(/[\]\r\n|<>]/g, " ").trim() || "—";
|
|
@@ -4388,11 +4460,11 @@ async function buildPrompt(enriched, branchName, worktreePath, client, workspace
|
|
|
4388
4460
|
Do NOT push to main. All your work stays on \`${branchName}\`.
|
|
4389
4461
|
When finished, call harmony_end_agent_session with status="completed".`
|
|
4390
4462
|
});
|
|
4391
|
-
log.info(
|
|
4463
|
+
log.info(TAG21, `Generated prompt for #${card.short_id} — ${result.contextSummary.memoryCount} memories, ${result.tokenEstimate} tokens`);
|
|
4392
4464
|
return result.prompt + pastEpisodesSection;
|
|
4393
4465
|
} catch (err) {
|
|
4394
4466
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4395
|
-
log.warn(
|
|
4467
|
+
log.warn(TAG21, `Failed to generate prompt via API, using fallback: ${msg}`);
|
|
4396
4468
|
const commentsSection = await renderCommentsSection(client, card.id);
|
|
4397
4469
|
return buildFallbackPrompt(enriched, branchName, worktreePath) + commentsSection + pastEpisodesSection;
|
|
4398
4470
|
}
|
|
@@ -4410,7 +4482,7 @@ async function renderCommentsSection(client, cardId) {
|
|
|
4410
4482
|
|
|
4411
4483
|
${section}` : "";
|
|
4412
4484
|
} catch (err) {
|
|
4413
|
-
log.warn(
|
|
4485
|
+
log.warn(TAG21, "comment-thread fetch failed", {
|
|
4414
4486
|
event: "comment_fetch_failed",
|
|
4415
4487
|
error: err instanceof Error ? err.message : String(err)
|
|
4416
4488
|
});
|
|
@@ -4460,7 +4532,7 @@ ${description}`.trim();
|
|
|
4460
4532
|
## Similar past tasks
|
|
4461
4533
|
${bullets}`;
|
|
4462
4534
|
} catch (err) {
|
|
4463
|
-
log.warn(
|
|
4535
|
+
log.warn(TAG21, "past-episodes recall failed", {
|
|
4464
4536
|
event: "episode_recall_failed",
|
|
4465
4537
|
error: err instanceof Error ? err.message : String(err)
|
|
4466
4538
|
});
|
|
@@ -4501,7 +4573,7 @@ ${subtaskStr}
|
|
|
4501
4573
|
You are working in a git worktree at \`${worktreePath}\` on branch \`${branchName}\`.
|
|
4502
4574
|
Do NOT push to main. All your work stays on \`${branchName}\`.`;
|
|
4503
4575
|
}
|
|
4504
|
-
var
|
|
4576
|
+
var TAG21 = "prompt";
|
|
4505
4577
|
var init_prompt = __esm(() => {
|
|
4506
4578
|
init_dist();
|
|
4507
4579
|
init_log();
|
|
@@ -4515,6 +4587,7 @@ class Worker {
|
|
|
4515
4587
|
workspaceId;
|
|
4516
4588
|
projectId;
|
|
4517
4589
|
stateStore;
|
|
4590
|
+
onCardCompleted;
|
|
4518
4591
|
id;
|
|
4519
4592
|
state = "idle";
|
|
4520
4593
|
cardId = null;
|
|
@@ -4530,13 +4603,14 @@ class Worker {
|
|
|
4530
4603
|
verificationFailed = false;
|
|
4531
4604
|
sessionId = null;
|
|
4532
4605
|
runId = null;
|
|
4533
|
-
constructor(id, config, client, _userEmail, onDone, workspaceId, projectId, stateStore) {
|
|
4606
|
+
constructor(id, config, client, _userEmail, onDone, workspaceId, projectId, stateStore, onCardCompleted) {
|
|
4534
4607
|
this.config = config;
|
|
4535
4608
|
this.client = client;
|
|
4536
4609
|
this.onDone = onDone;
|
|
4537
4610
|
this.workspaceId = workspaceId;
|
|
4538
4611
|
this.projectId = projectId;
|
|
4539
4612
|
this.stateStore = stateStore;
|
|
4613
|
+
this.onCardCompleted = onCardCompleted;
|
|
4540
4614
|
this.id = id;
|
|
4541
4615
|
}
|
|
4542
4616
|
startHeartbeat() {
|
|
@@ -4571,7 +4645,7 @@ class Worker {
|
|
|
4571
4645
|
}
|
|
4572
4646
|
}
|
|
4573
4647
|
get tag() {
|
|
4574
|
-
return `${
|
|
4648
|
+
return `${TAG22}:${this.id}`;
|
|
4575
4649
|
}
|
|
4576
4650
|
get isIdle() {
|
|
4577
4651
|
return this.state === "idle";
|
|
@@ -4617,7 +4691,7 @@ class Worker {
|
|
|
4617
4691
|
});
|
|
4618
4692
|
const sid = session && typeof session === "object" && "id" in session ? session.id : null;
|
|
4619
4693
|
if (!sid) {
|
|
4620
|
-
log.warn(
|
|
4694
|
+
log.warn(TAG22, "startAgentSession returned no session id");
|
|
4621
4695
|
}
|
|
4622
4696
|
this.sessionId = sid;
|
|
4623
4697
|
await this.recordPhase("preparing");
|
|
@@ -4666,7 +4740,7 @@ class Worker {
|
|
|
4666
4740
|
});
|
|
4667
4741
|
this.state = "completing";
|
|
4668
4742
|
await this.recordPhase("completing");
|
|
4669
|
-
const completed = await runCompletion(this.client, card, this.branchName, this.worktreePath, this.config, this.id, this.lastSessionStats, this.workspaceId, this.sessionId, this.stateStore);
|
|
4743
|
+
const completed = await runCompletion(this.client, card, this.branchName, this.worktreePath, this.config, this.id, this.lastSessionStats, this.workspaceId, this.sessionId, this.stateStore, this.onCardCompleted);
|
|
4670
4744
|
this.verificationFailed = !completed;
|
|
4671
4745
|
} catch (err) {
|
|
4672
4746
|
this.state = "error";
|
|
@@ -4911,7 +4985,7 @@ class Worker {
|
|
|
4911
4985
|
this.sessionId = null;
|
|
4912
4986
|
}
|
|
4913
4987
|
}
|
|
4914
|
-
var
|
|
4988
|
+
var TAG22 = "worker", CANCEL_SIGINT_TIMEOUT2 = 30000, CANCEL_SIGTERM_TIMEOUT2 = 1e4;
|
|
4915
4989
|
var init_worker = __esm(() => {
|
|
4916
4990
|
init_board_helpers();
|
|
4917
4991
|
init_completion();
|
|
@@ -4930,14 +5004,17 @@ var init_worker = __esm(() => {
|
|
|
4930
5004
|
// src/pool.ts
|
|
4931
5005
|
class Pool {
|
|
4932
5006
|
client;
|
|
5007
|
+
projectId;
|
|
4933
5008
|
stateStore;
|
|
4934
5009
|
implWorkers = [];
|
|
4935
5010
|
reviewWorkers = [];
|
|
4936
5011
|
implQueue;
|
|
4937
5012
|
reviewQueue;
|
|
4938
5013
|
budget;
|
|
5014
|
+
onCardCompleted = null;
|
|
4939
5015
|
constructor(config, client, userEmail, workspaceId, projectId, stateStore) {
|
|
4940
5016
|
this.client = client;
|
|
5017
|
+
this.projectId = projectId;
|
|
4941
5018
|
this.stateStore = stateStore;
|
|
4942
5019
|
this.implQueue = new PriorityQueue(config);
|
|
4943
5020
|
this.reviewQueue = new PriorityQueue(config);
|
|
@@ -4945,7 +5022,9 @@ class Pool {
|
|
|
4945
5022
|
for (let i = 0;i < config.poolSize; i++) {
|
|
4946
5023
|
this.implWorkers.push(new Worker(i, config, client, userEmail, () => {
|
|
4947
5024
|
this.tryDispatchFor(this.implWorkers, this.implQueue, "impl");
|
|
4948
|
-
}, workspaceId, projectId, stateStore)
|
|
5025
|
+
}, workspaceId, projectId, stateStore, async (completedCard) => {
|
|
5026
|
+
await this.onCardCompleted?.(completedCard);
|
|
5027
|
+
}));
|
|
4949
5028
|
}
|
|
4950
5029
|
if (config.review.enabled) {
|
|
4951
5030
|
for (let i = 0;i < config.review.poolSize; i++) {
|
|
@@ -4958,20 +5037,31 @@ class Pool {
|
|
|
4958
5037
|
}
|
|
4959
5038
|
async enqueue(card, column, labels, subtasks, mode = "implement") {
|
|
4960
5039
|
if (this.implQueue.has(card.id) || this.reviewQueue.has(card.id) || this.isCardActive(card.id)) {
|
|
4961
|
-
log.debug(
|
|
5040
|
+
log.debug(TAG23, `Card ${card.id} already queued or active, skipping`);
|
|
4962
5041
|
return;
|
|
4963
5042
|
}
|
|
4964
5043
|
if (mode === "implement") {
|
|
4965
5044
|
const decision = this.budget.check(card.id);
|
|
4966
5045
|
if (!decision.allow) {
|
|
4967
5046
|
if (decision.reason === "daily_budget") {
|
|
4968
|
-
log.warn(
|
|
5047
|
+
log.warn(TAG23, `#${card.short_id} skipped (daily_budget): ${decision.detail}`);
|
|
4969
5048
|
await this.emitWaiting(card.id, `Daily budget reached — waiting for reset (${decision.detail})`);
|
|
4970
5049
|
} else {
|
|
4971
|
-
log.debug(
|
|
5050
|
+
log.debug(TAG23, `#${card.short_id} gave up: ${decision.detail}`);
|
|
4972
5051
|
}
|
|
4973
5052
|
return;
|
|
4974
5053
|
}
|
|
5054
|
+
const blockers = await getUnresolvedBlockers(this.client, card, this.projectId);
|
|
5055
|
+
if (blockers === null) {
|
|
5056
|
+
log.warn(TAG23, `#${card.short_id} blocker check failed — deferring to next tick`);
|
|
5057
|
+
return;
|
|
5058
|
+
}
|
|
5059
|
+
if (blockers.length > 0) {
|
|
5060
|
+
const list = blockers.map((b) => `#${b.shortId}`).join(", ");
|
|
5061
|
+
log.info(TAG23, `#${card.short_id} blocked by ${list} — waiting`);
|
|
5062
|
+
await this.emitWaiting(card.id, `Blocked by ${list} — waiting for chain`);
|
|
5063
|
+
return;
|
|
5064
|
+
}
|
|
4975
5065
|
}
|
|
4976
5066
|
const queue = mode === "review" ? this.reviewQueue : this.implQueue;
|
|
4977
5067
|
queue.enqueue(card, column, labels, mode);
|
|
@@ -4984,7 +5074,10 @@ class Pool {
|
|
|
4984
5074
|
await this.emitWaiting(card.id, position > 0 ? `Queued (${position}/${total}) — waiting for ${mode} worker` : `Queued — waiting for ${mode} worker`);
|
|
4985
5075
|
}
|
|
4986
5076
|
}
|
|
5077
|
+
lastWaitingEmit = new Map;
|
|
4987
5078
|
async emitWaiting(cardId, currentTask) {
|
|
5079
|
+
if (this.lastWaitingEmit.get(cardId) === currentTask)
|
|
5080
|
+
return;
|
|
4988
5081
|
try {
|
|
4989
5082
|
await this.client.updateAgentProgress(cardId, {
|
|
4990
5083
|
agentIdentifier: agentIdentifier(0),
|
|
@@ -4992,23 +5085,25 @@ class Pool {
|
|
|
4992
5085
|
status: "waiting",
|
|
4993
5086
|
currentTask
|
|
4994
5087
|
});
|
|
5088
|
+
this.lastWaitingEmit.set(cardId, currentTask);
|
|
4995
5089
|
} catch (err) {
|
|
4996
|
-
log.debug(
|
|
5090
|
+
log.debug(TAG23, `waiting emit failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
|
|
4997
5091
|
}
|
|
4998
5092
|
}
|
|
4999
5093
|
async removeCard(cardId) {
|
|
5000
5094
|
await this.stateStore.resetAttempts(cardId);
|
|
5095
|
+
this.lastWaitingEmit.delete(cardId);
|
|
5001
5096
|
for (const queue of [this.implQueue, this.reviewQueue]) {
|
|
5002
5097
|
const removed = queue.remove(cardId);
|
|
5003
5098
|
if (removed) {
|
|
5004
5099
|
this.cardDataCache.delete(cardId);
|
|
5005
|
-
log.info(
|
|
5100
|
+
log.info(TAG23, `Removed #${removed.shortId} from ${removed.mode} queue`);
|
|
5006
5101
|
return;
|
|
5007
5102
|
}
|
|
5008
5103
|
}
|
|
5009
5104
|
const worker = this.implWorkers.find((w) => w.cardId === cardId) ?? this.reviewWorkers.find((w) => w.cardId === cardId);
|
|
5010
5105
|
if (worker) {
|
|
5011
|
-
log.info(
|
|
5106
|
+
log.info(TAG23, `Cancelling worker ${worker.id} for card ${cardId}`);
|
|
5012
5107
|
await worker.cancel();
|
|
5013
5108
|
}
|
|
5014
5109
|
}
|
|
@@ -5036,10 +5131,10 @@ class Pool {
|
|
|
5036
5131
|
async handleAgentCommand(cardId, command) {
|
|
5037
5132
|
const worker = this.implWorkers.find((w) => w.cardId === cardId && w.isActive) ?? this.reviewWorkers.find((w) => w.cardId === cardId && w.isActive);
|
|
5038
5133
|
if (!worker) {
|
|
5039
|
-
log.debug(
|
|
5134
|
+
log.debug(TAG23, `No active worker for card ${cardId}, ignoring ${command}`);
|
|
5040
5135
|
return;
|
|
5041
5136
|
}
|
|
5042
|
-
log.info(
|
|
5137
|
+
log.info(TAG23, `Agent command: ${command} → worker ${worker.id} (card ${cardId})`);
|
|
5043
5138
|
switch (command) {
|
|
5044
5139
|
case "pause":
|
|
5045
5140
|
await worker.pause();
|
|
@@ -5085,19 +5180,19 @@ class Pool {
|
|
|
5085
5180
|
};
|
|
5086
5181
|
}
|
|
5087
5182
|
async shutdown() {
|
|
5088
|
-
log.info(
|
|
5183
|
+
log.info(TAG23, "Shutting down pool...");
|
|
5089
5184
|
const active = [
|
|
5090
5185
|
...this.implWorkers.filter((w) => w.isActive),
|
|
5091
5186
|
...this.reviewWorkers.filter((w) => w.isActive)
|
|
5092
5187
|
];
|
|
5093
5188
|
await Promise.all(active.map((w) => w.cancel()));
|
|
5094
|
-
log.info(
|
|
5189
|
+
log.info(TAG23, "Pool shutdown complete");
|
|
5095
5190
|
}
|
|
5096
5191
|
cardDataCache = new Map;
|
|
5097
5192
|
tryDispatchFor(workers, queue, label) {
|
|
5098
5193
|
const idle = workers.find((w) => w.isIdle);
|
|
5099
5194
|
if (!idle) {
|
|
5100
|
-
log.debug(
|
|
5195
|
+
log.debug(TAG23, `No idle ${label} workers (queue: ${queue.length})`);
|
|
5101
5196
|
return false;
|
|
5102
5197
|
}
|
|
5103
5198
|
const next = queue.dequeue();
|
|
@@ -5105,21 +5200,23 @@ class Pool {
|
|
|
5105
5200
|
return false;
|
|
5106
5201
|
const data = this.cardDataCache.get(next.cardId);
|
|
5107
5202
|
if (!data) {
|
|
5108
|
-
log.warn(
|
|
5203
|
+
log.warn(TAG23, `No cached data for card ${next.cardId}, skipping`);
|
|
5109
5204
|
return false;
|
|
5110
5205
|
}
|
|
5111
5206
|
this.cardDataCache.delete(next.cardId);
|
|
5112
|
-
|
|
5207
|
+
this.lastWaitingEmit.delete(next.cardId);
|
|
5208
|
+
log.info(TAG23, `Dispatching #${next.shortId} to ${label} worker ${idle.id}`);
|
|
5113
5209
|
idle.run(data.card, data.column, data.labels, data.subtasks);
|
|
5114
5210
|
return true;
|
|
5115
5211
|
}
|
|
5116
5212
|
}
|
|
5117
|
-
var
|
|
5213
|
+
var TAG23 = "pool";
|
|
5118
5214
|
var init_pool = __esm(() => {
|
|
5119
5215
|
init_log();
|
|
5120
5216
|
init_queue();
|
|
5121
5217
|
init_review_worker();
|
|
5122
5218
|
init_types();
|
|
5219
|
+
init_unblock();
|
|
5123
5220
|
init_worker();
|
|
5124
5221
|
});
|
|
5125
5222
|
|
|
@@ -5139,7 +5236,7 @@ async function fetchCardSafely(client, cardId) {
|
|
|
5139
5236
|
const { card } = await client.getCard(cardId);
|
|
5140
5237
|
return card;
|
|
5141
5238
|
} catch (err) {
|
|
5142
|
-
log.warn(
|
|
5239
|
+
log.warn(TAG24, `cannot fetch card ${cardId}: ${err instanceof Error ? err.message : err}`);
|
|
5143
5240
|
return null;
|
|
5144
5241
|
}
|
|
5145
5242
|
}
|
|
@@ -5149,7 +5246,7 @@ async function recoverOrphans(store, client, config) {
|
|
|
5149
5246
|
return [];
|
|
5150
5247
|
}
|
|
5151
5248
|
const outcomes = [];
|
|
5152
|
-
log.info(
|
|
5249
|
+
log.info(TAG24, `recovering ${active.length} orphan run(s) from prior daemon`);
|
|
5153
5250
|
for (const run of active) {
|
|
5154
5251
|
const outcome = {
|
|
5155
5252
|
runId: run.runId,
|
|
@@ -5161,11 +5258,11 @@ async function recoverOrphans(store, client, config) {
|
|
|
5161
5258
|
};
|
|
5162
5259
|
outcomes.push(outcome);
|
|
5163
5260
|
if (isProcessAlive(run.daemonPid, process.pid)) {
|
|
5164
|
-
log.warn(
|
|
5261
|
+
log.warn(TAG24, `run ${run.runId} claims live daemon pid ${run.daemonPid} — skipping`);
|
|
5165
5262
|
outcome.actions.push("skipped: daemon pid still alive");
|
|
5166
5263
|
continue;
|
|
5167
5264
|
}
|
|
5168
|
-
log.info(
|
|
5265
|
+
log.info(TAG24, `recovering ${run.pipeline} run ${run.runId} for card #${run.cardShortId}`);
|
|
5169
5266
|
await recoverRun(run, store, client, config, outcome);
|
|
5170
5267
|
}
|
|
5171
5268
|
return outcomes;
|
|
@@ -5183,7 +5280,7 @@ async function recoverRun(run, store, client, config, outcome) {
|
|
|
5183
5280
|
} catch (err) {
|
|
5184
5281
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5185
5282
|
outcome.errors.push(`endAgentSession: ${msg}`);
|
|
5186
|
-
log.warn(
|
|
5283
|
+
log.warn(TAG24, `endAgentSession failed for ${run.cardId}: ${msg}`);
|
|
5187
5284
|
}
|
|
5188
5285
|
const card = await fetchCardSafely(client, run.cardId);
|
|
5189
5286
|
if (card) {
|
|
@@ -5224,9 +5321,9 @@ async function recoverRun(run, store, client, config, outcome) {
|
|
|
5224
5321
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5225
5322
|
outcome.errors.push(`endRun: ${msg}`);
|
|
5226
5323
|
}
|
|
5227
|
-
log.info(
|
|
5324
|
+
log.info(TAG24, `recovered run ${run.runId} (card #${run.cardShortId}): ${outcome.actions.join(", ")}${outcome.errors.length ? ` | errors: ${outcome.errors.join("; ")}` : ""}`);
|
|
5228
5325
|
}
|
|
5229
|
-
var
|
|
5326
|
+
var TAG24 = "recovery", RECOVERED_LABEL = "agent-recovered", RECOVERED_LABEL_COLOR = "#f59e0b";
|
|
5230
5327
|
var init_recovery = __esm(() => {
|
|
5231
5328
|
init_board_helpers();
|
|
5232
5329
|
init_log();
|
|
@@ -5274,7 +5371,7 @@ class Reconciler {
|
|
|
5274
5371
|
clearInterval(this.timer);
|
|
5275
5372
|
this.timer = null;
|
|
5276
5373
|
}
|
|
5277
|
-
log.info(
|
|
5374
|
+
log.info(TAG25, "Heartbeat stopped");
|
|
5278
5375
|
}
|
|
5279
5376
|
async recoverStaleRuns() {
|
|
5280
5377
|
if (!this.stateStore || !this.agentConfig)
|
|
@@ -5291,7 +5388,7 @@ class Reconciler {
|
|
|
5291
5388
|
if (!daemonDead && !(heartbeatStale && ourZombie))
|
|
5292
5389
|
continue;
|
|
5293
5390
|
const reason = daemonDead ? `foreign daemon ${run.daemonPid} is dead` : `our worker lost card ${run.cardId} with ${Math.round((now - run.lastHeartbeatAt) / 1000)}s stale heartbeat`;
|
|
5294
|
-
log.warn(
|
|
5391
|
+
log.warn(TAG25, `zombie run ${run.runId} (#${run.cardShortId}): ${reason} — recovering`);
|
|
5295
5392
|
await recoverRun(run, this.stateStore, this.client, this.agentConfig, {
|
|
5296
5393
|
runId: run.runId,
|
|
5297
5394
|
cardId: run.cardId,
|
|
@@ -5327,18 +5424,18 @@ class Reconciler {
|
|
|
5327
5424
|
const subtasks = card.subtasks ?? [];
|
|
5328
5425
|
const mode = reviewColumnIds.has(card.column_id) ? "review" : "implement";
|
|
5329
5426
|
if (mode === "review" && this.approvedLabel && hasLabel(cardLabels, this.approvedLabel)) {
|
|
5330
|
-
log.debug(
|
|
5427
|
+
log.debug(TAG25, `Skipping #${card.short_id} — already has "${this.approvedLabel}" label`);
|
|
5331
5428
|
continue;
|
|
5332
5429
|
}
|
|
5333
5430
|
if (mode === "review" && hasLabel(cardLabels, NEED_REVIEW_LABEL)) {
|
|
5334
|
-
log.debug(
|
|
5431
|
+
log.debug(TAG25, `Skipping #${card.short_id} — has "${NEED_REVIEW_LABEL}" label (needs human)`);
|
|
5335
5432
|
continue;
|
|
5336
5433
|
}
|
|
5337
5434
|
if (mode === "review" && !extractBranchFromDescription(card.description)) {
|
|
5338
|
-
log.debug(
|
|
5435
|
+
log.debug(TAG25, `Skipping #${card.short_id} — no branch reference (not qualified for auto-review)`);
|
|
5339
5436
|
continue;
|
|
5340
5437
|
}
|
|
5341
|
-
log.info(
|
|
5438
|
+
log.info(TAG25, `Missed assignment: #${card.short_id} "${card.title}" (${mode}) — enqueueing`);
|
|
5342
5439
|
await this.pool.enqueue(card, column, cardLabels, subtasks, mode);
|
|
5343
5440
|
}
|
|
5344
5441
|
}
|
|
@@ -5347,17 +5444,17 @@ class Reconciler {
|
|
|
5347
5444
|
}
|
|
5348
5445
|
for (const knownId of knownCardIds) {
|
|
5349
5446
|
if (!allAgentCardIds.has(knownId)) {
|
|
5350
|
-
log.info(
|
|
5447
|
+
log.info(TAG25, `Missed unassign: ${knownId} — removing`);
|
|
5351
5448
|
await this.pool.removeCard(knownId);
|
|
5352
5449
|
}
|
|
5353
5450
|
}
|
|
5354
|
-
log.debug(
|
|
5451
|
+
log.debug(TAG25, `Reconciled: ${assignedCards.length} assigned, ${knownCardIds.size} known`);
|
|
5355
5452
|
} catch (err) {
|
|
5356
|
-
log.error(
|
|
5453
|
+
log.error(TAG25, `Heartbeat failed: ${err instanceof Error ? err.message : err}`);
|
|
5357
5454
|
}
|
|
5358
5455
|
}
|
|
5359
5456
|
}
|
|
5360
|
-
var
|
|
5457
|
+
var TAG25 = "reconcile";
|
|
5361
5458
|
var init_reconcile = __esm(() => {
|
|
5362
5459
|
init_board_helpers();
|
|
5363
5460
|
init_log();
|
|
@@ -5391,7 +5488,7 @@ function prettyBanner(config, version) {
|
|
|
5391
5488
|
checks.push({ kind: "ok", message });
|
|
5392
5489
|
},
|
|
5393
5490
|
warn(message) {
|
|
5394
|
-
log.warn(
|
|
5491
|
+
log.warn(TAG26, message);
|
|
5395
5492
|
checks.push({ kind: "warn", message: message.split(`
|
|
5396
5493
|
`, 1)[0] });
|
|
5397
5494
|
},
|
|
@@ -5415,22 +5512,22 @@ function prettyBanner(config, version) {
|
|
|
5415
5512
|
};
|
|
5416
5513
|
}
|
|
5417
5514
|
function jsonBanner(config, version) {
|
|
5418
|
-
log.info(
|
|
5419
|
-
log.info(
|
|
5515
|
+
log.info(TAG26, `Harmony Agent Daemon v${version} starting...`);
|
|
5516
|
+
log.info(TAG26, `Project: ${config.projectId} | Pool: ${config.agent.poolSize} | Model: ${config.agent.claude.model} | Pickup: ${config.agent.pickupColumns.join(", ")}`);
|
|
5420
5517
|
if (config.agent.review.enabled) {
|
|
5421
|
-
log.info(
|
|
5518
|
+
log.info(TAG26, `Review: enabled | Columns: ${config.agent.review.pickupColumns.join(", ")} | → ${config.agent.review.moveToColumn} / ${config.agent.review.failColumn}`);
|
|
5422
5519
|
}
|
|
5423
5520
|
let failed = false;
|
|
5424
5521
|
return {
|
|
5425
5522
|
setProjectName(_name) {},
|
|
5426
5523
|
setGitProvider(provider) {
|
|
5427
|
-
log.info(
|
|
5524
|
+
log.info(TAG26, `Git provider: ${provider}`);
|
|
5428
5525
|
},
|
|
5429
5526
|
check(message) {
|
|
5430
|
-
log.info(
|
|
5527
|
+
log.info(TAG26, message);
|
|
5431
5528
|
},
|
|
5432
5529
|
warn(message) {
|
|
5433
|
-
log.warn(
|
|
5530
|
+
log.warn(TAG26, message);
|
|
5434
5531
|
},
|
|
5435
5532
|
fail() {
|
|
5436
5533
|
failed = true;
|
|
@@ -5438,7 +5535,7 @@ function jsonBanner(config, version) {
|
|
|
5438
5535
|
async ready(message) {
|
|
5439
5536
|
if (failed)
|
|
5440
5537
|
return;
|
|
5441
|
-
log.info(
|
|
5538
|
+
log.info(TAG26, message);
|
|
5442
5539
|
}
|
|
5443
5540
|
};
|
|
5444
5541
|
}
|
|
@@ -5505,7 +5602,7 @@ function cyan(s) {
|
|
|
5505
5602
|
function yellow(s) {
|
|
5506
5603
|
return `${ANSI.yellow}${s}${ANSI.reset}`;
|
|
5507
5604
|
}
|
|
5508
|
-
var
|
|
5605
|
+
var TAG26 = "daemon", RULE_WIDTH = 70, ANSI;
|
|
5509
5606
|
var init_startup_banner = __esm(() => {
|
|
5510
5607
|
init_log();
|
|
5511
5608
|
ANSI = {
|
|
@@ -5652,18 +5749,18 @@ class Watcher {
|
|
|
5652
5749
|
}
|
|
5653
5750
|
async start() {
|
|
5654
5751
|
if (!isPretty()) {
|
|
5655
|
-
log.info(
|
|
5752
|
+
log.info(TAG27, "Connecting to Supabase realtime (broadcast)...");
|
|
5656
5753
|
}
|
|
5657
5754
|
this.supabase = createClient(this.credentials.supabaseUrl, this.credentials.supabaseAnonKey);
|
|
5658
5755
|
const presenceChannel = this.supabase.channel(`board-presence-${this.projectId}`);
|
|
5659
5756
|
const channel = this.supabase.channel(`board-${this.projectId}`).on("broadcast", { event: "card_update" }, (msg) => {
|
|
5660
|
-
log.debug(
|
|
5757
|
+
log.debug(TAG27, `Broadcast: card_update ${JSON.stringify(msg.payload)}`);
|
|
5661
5758
|
this.onCardBroadcast({
|
|
5662
5759
|
event: "card_update",
|
|
5663
5760
|
payload: msg.payload ?? {}
|
|
5664
5761
|
});
|
|
5665
5762
|
}).on("broadcast", { event: "card_created" }, (msg) => {
|
|
5666
|
-
log.debug(
|
|
5763
|
+
log.debug(TAG27, `Broadcast: card_created ${JSON.stringify(msg.payload)}`);
|
|
5667
5764
|
this.onCardBroadcast({
|
|
5668
5765
|
event: "card_created",
|
|
5669
5766
|
payload: msg.payload ?? {}
|
|
@@ -5673,29 +5770,29 @@ class Watcher {
|
|
|
5673
5770
|
const cardId = payload.card_id;
|
|
5674
5771
|
const command = payload.command;
|
|
5675
5772
|
if (cardId && command) {
|
|
5676
|
-
log.info(
|
|
5773
|
+
log.info(TAG27, `Broadcast: agent_command ${command} for ${cardId}`);
|
|
5677
5774
|
this.onAgentCommand?.({ cardId, command });
|
|
5678
5775
|
}
|
|
5679
5776
|
}).subscribe((status) => {
|
|
5680
5777
|
if (status === "SUBSCRIBED") {
|
|
5681
5778
|
this.connected = true;
|
|
5682
5779
|
if (!isPretty() || !this.suppressStartupLogs) {
|
|
5683
|
-
log.info(
|
|
5780
|
+
log.info(TAG27, "Broadcast subscription active");
|
|
5684
5781
|
}
|
|
5685
5782
|
this.maybeResolveReady();
|
|
5686
5783
|
} else if (status === "CHANNEL_ERROR") {
|
|
5687
5784
|
this.connected = false;
|
|
5688
|
-
log.error(
|
|
5785
|
+
log.error(TAG27, "Broadcast channel error — will rely on reconciliation");
|
|
5689
5786
|
} else if (status === "TIMED_OUT") {
|
|
5690
5787
|
this.connected = false;
|
|
5691
|
-
log.warn(
|
|
5788
|
+
log.warn(TAG27, "Broadcast subscription timed out — retrying...");
|
|
5692
5789
|
} else if (status === "CLOSED") {
|
|
5693
5790
|
this.connected = false;
|
|
5694
5791
|
}
|
|
5695
5792
|
});
|
|
5696
5793
|
this.channel = channel;
|
|
5697
5794
|
presenceChannel.on("presence", { event: "sync" }, () => {
|
|
5698
|
-
log.debug(
|
|
5795
|
+
log.debug(TAG27, "Presence sync");
|
|
5699
5796
|
}).subscribe(async (status) => {
|
|
5700
5797
|
if (status === "SUBSCRIBED") {
|
|
5701
5798
|
await presenceChannel.track({
|
|
@@ -5707,7 +5804,7 @@ class Watcher {
|
|
|
5707
5804
|
agentName: this.identity.agentName
|
|
5708
5805
|
});
|
|
5709
5806
|
if (!isPretty() || !this.suppressStartupLogs) {
|
|
5710
|
-
log.info(
|
|
5807
|
+
log.info(TAG27, "Presence tracked on board-presence channel");
|
|
5711
5808
|
}
|
|
5712
5809
|
this.presenceTracked = true;
|
|
5713
5810
|
this.maybeResolveReady();
|
|
@@ -5729,10 +5826,10 @@ class Watcher {
|
|
|
5729
5826
|
this.supabase = null;
|
|
5730
5827
|
}
|
|
5731
5828
|
this.connected = false;
|
|
5732
|
-
log.info(
|
|
5829
|
+
log.info(TAG27, "Broadcast subscription stopped");
|
|
5733
5830
|
}
|
|
5734
5831
|
}
|
|
5735
|
-
var
|
|
5832
|
+
var TAG27 = "watcher";
|
|
5736
5833
|
var init_watcher = __esm(() => {
|
|
5737
5834
|
init_log();
|
|
5738
5835
|
});
|
|
@@ -5807,10 +5904,10 @@ function runWorktreeGc(basePath, store, opts = {}) {
|
|
|
5807
5904
|
});
|
|
5808
5905
|
} catch {}
|
|
5809
5906
|
if (result.removed.length > 0) {
|
|
5810
|
-
log.info(
|
|
5907
|
+
log.info(TAG28, `GC removed ${result.removed.length} orphan worktree(s): ${result.removed.map((p) => p.split("/").pop()).join(", ")}`);
|
|
5811
5908
|
}
|
|
5812
5909
|
if (result.errors.length > 0) {
|
|
5813
|
-
log.warn(
|
|
5910
|
+
log.warn(TAG28, `GC had ${result.errors.length} error(s): ${result.errors.map((e) => `${e.path}: ${e.error}`).join("; ")}`);
|
|
5814
5911
|
}
|
|
5815
5912
|
return result;
|
|
5816
5913
|
}
|
|
@@ -5887,10 +5984,10 @@ function pruneFailedRemoteBranches(opts) {
|
|
|
5887
5984
|
}
|
|
5888
5985
|
}
|
|
5889
5986
|
if (result.removed.length > 0) {
|
|
5890
|
-
log.info(
|
|
5987
|
+
log.info(TAG28, `Pruned ${result.removed.length} stale remote branch(es) under ${opts.prefix}: ${result.removed.join(", ")}`);
|
|
5891
5988
|
}
|
|
5892
5989
|
if (result.errors.length > 0) {
|
|
5893
|
-
log.warn(
|
|
5990
|
+
log.warn(TAG28, `Remote branch GC had ${result.errors.length} error(s): ${result.errors.map((e) => `${e.ref}: ${e.error}`).join("; ")}`);
|
|
5894
5991
|
}
|
|
5895
5992
|
return result;
|
|
5896
5993
|
}
|
|
@@ -5921,13 +6018,13 @@ class WorktreeGc {
|
|
|
5921
6018
|
try {
|
|
5922
6019
|
runWorktreeGc(this.basePath, this.store);
|
|
5923
6020
|
} catch (err) {
|
|
5924
|
-
log.warn(
|
|
6021
|
+
log.warn(TAG28, `GC tick failed: ${err instanceof Error ? err.message : err}`);
|
|
5925
6022
|
}
|
|
5926
6023
|
if (this.remoteOpts) {
|
|
5927
6024
|
try {
|
|
5928
6025
|
pruneFailedRemoteBranches(this.remoteOpts);
|
|
5929
6026
|
} catch (err) {
|
|
5930
|
-
log.warn(
|
|
6027
|
+
log.warn(TAG28, `Remote GC tick failed: ${err instanceof Error ? err.message : err}`);
|
|
5931
6028
|
}
|
|
5932
6029
|
}
|
|
5933
6030
|
}
|
|
@@ -5941,7 +6038,7 @@ function getRepoRoot2() {
|
|
|
5941
6038
|
return null;
|
|
5942
6039
|
}
|
|
5943
6040
|
}
|
|
5944
|
-
var
|
|
6041
|
+
var TAG28 = "worktree-gc";
|
|
5945
6042
|
var init_worktree_gc = __esm(() => {
|
|
5946
6043
|
init_log();
|
|
5947
6044
|
init_worktree();
|
|
@@ -6023,7 +6120,7 @@ async function main() {
|
|
|
6023
6120
|
} catch (err) {
|
|
6024
6121
|
if (err instanceof ConfigValidationError) {
|
|
6025
6122
|
banner.fail();
|
|
6026
|
-
log.error(
|
|
6123
|
+
log.error(TAG29, err.message);
|
|
6027
6124
|
process.exit(1);
|
|
6028
6125
|
}
|
|
6029
6126
|
throw err;
|
|
@@ -6041,12 +6138,21 @@ async function main() {
|
|
|
6041
6138
|
const realtimeCreds = await fetchRealtimeCredentials(client);
|
|
6042
6139
|
banner.check("Realtime credentials");
|
|
6043
6140
|
const pool = new Pool(config.agent, client, config.userEmail, config.workspaceId, config.projectId, stateStore);
|
|
6141
|
+
const promoteSuccessors = async (completedCard) => {
|
|
6142
|
+
await promoteUnblockedSuccessors(completedCard, {
|
|
6143
|
+
client,
|
|
6144
|
+
agentUserId,
|
|
6145
|
+
enqueue: (cardId) => tryEnqueueCard(cardId, client, pool, config)
|
|
6146
|
+
});
|
|
6147
|
+
};
|
|
6148
|
+
pool.onCardCompleted = promoteSuccessors;
|
|
6044
6149
|
const reviewColumns = config.agent.review.enabled ? config.agent.review.pickupColumns : [];
|
|
6045
6150
|
const approvedLabel = config.agent.review.enabled ? config.agent.review.approvedLabel : "";
|
|
6046
6151
|
const reconciler = new Reconciler(client, pool, config.projectId, agentUserId, config.agent.pickupColumns, reviewColumns, approvedLabel, config.agent.timing.reconcileIntervalMs, stateStore, config.agent);
|
|
6047
6152
|
let mergeMonitor = null;
|
|
6048
6153
|
if (config.agent.review.enabled && config.agent.review.mergeMonitor) {
|
|
6049
6154
|
mergeMonitor = new MergeMonitor(client, config.projectId, config.agent);
|
|
6155
|
+
mergeMonitor.onCardCompleted = promoteSuccessors;
|
|
6050
6156
|
}
|
|
6051
6157
|
const worktreeGc = new WorktreeGc(config.agent.worktree.basePath, stateStore, config.agent.timing.worktreeGcIntervalMs, config.agent.worktree.failedAttemptRetentionDays > 0 ? {
|
|
6052
6158
|
prefix: config.agent.worktree.failedBranchPrefix,
|
|
@@ -6115,25 +6221,25 @@ async function main() {
|
|
|
6115
6221
|
if (shuttingDown)
|
|
6116
6222
|
return;
|
|
6117
6223
|
shuttingDown = true;
|
|
6118
|
-
log.info(
|
|
6224
|
+
log.info(TAG29, `Received ${signal}, shutting down gracefully...`);
|
|
6119
6225
|
reconciler.stop();
|
|
6120
6226
|
mergeMonitor?.stop();
|
|
6121
6227
|
worktreeGc.stop();
|
|
6122
6228
|
await httpServer?.stop();
|
|
6123
6229
|
await watcher.stop();
|
|
6124
6230
|
await pool.shutdown();
|
|
6125
|
-
log.info(
|
|
6231
|
+
log.info(TAG29, "Daemon stopped.");
|
|
6126
6232
|
process.exit(exitCode);
|
|
6127
6233
|
};
|
|
6128
6234
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
6129
6235
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
6130
6236
|
process.on("uncaughtException", (err) => {
|
|
6131
|
-
log.error(
|
|
6237
|
+
log.error(TAG29, `Uncaught exception: ${err.message}`);
|
|
6132
6238
|
exitCode = 1;
|
|
6133
6239
|
shutdown("uncaughtException");
|
|
6134
6240
|
});
|
|
6135
6241
|
process.on("unhandledRejection", (reason) => {
|
|
6136
|
-
log.error(
|
|
6242
|
+
log.error(TAG29, `Unhandled rejection: ${reason instanceof Error ? reason.message : String(reason)}`);
|
|
6137
6243
|
exitCode = 1;
|
|
6138
6244
|
shutdown("unhandledRejection");
|
|
6139
6245
|
});
|
|
@@ -6180,14 +6286,14 @@ async function handleBroadcast(event, client, pool, config, agentUserId) {
|
|
|
6180
6286
|
if (assigneeId === undefined)
|
|
6181
6287
|
return;
|
|
6182
6288
|
if (assigneeId === agentUserId) {
|
|
6183
|
-
log.info(
|
|
6289
|
+
log.info(TAG29, `Broadcast: card ${cardId} assigned to agent`);
|
|
6184
6290
|
try {
|
|
6185
6291
|
await tryEnqueueCard(cardId, client, pool, config);
|
|
6186
6292
|
} catch (err) {
|
|
6187
|
-
log.error(
|
|
6293
|
+
log.error(TAG29, `Failed to process assignment: ${err instanceof Error ? err.message : err}`);
|
|
6188
6294
|
}
|
|
6189
6295
|
} else if (pool.isCardKnown(cardId)) {
|
|
6190
|
-
log.info(
|
|
6296
|
+
log.info(TAG29, `Broadcast: card ${cardId} unassigned from agent`);
|
|
6191
6297
|
await pool.removeCard(cardId);
|
|
6192
6298
|
}
|
|
6193
6299
|
}
|
|
@@ -6197,13 +6303,13 @@ async function tryEnqueueCard(cardId, client, pool, config) {
|
|
|
6197
6303
|
const columns = board.columns;
|
|
6198
6304
|
const column = columns.find((c) => c.id === card.column_id);
|
|
6199
6305
|
if (!column) {
|
|
6200
|
-
log.warn(
|
|
6306
|
+
log.warn(TAG29, `Column not found for card ${cardId}`);
|
|
6201
6307
|
return;
|
|
6202
6308
|
}
|
|
6203
6309
|
const isPickupColumn = config.agent.pickupColumns.some((name) => name.toLowerCase() === column.name.toLowerCase());
|
|
6204
6310
|
const isReviewColumn = config.agent.review.enabled && config.agent.review.pickupColumns.some((name) => name.toLowerCase() === column.name.toLowerCase());
|
|
6205
6311
|
if (!isPickupColumn && !isReviewColumn) {
|
|
6206
|
-
log.info(
|
|
6312
|
+
log.info(TAG29, `Card #${card.short_id} is in "${column.name}", not a pickup/review column — skipping`);
|
|
6207
6313
|
return;
|
|
6208
6314
|
}
|
|
6209
6315
|
const mode = isReviewColumn ? "review" : "implement";
|
|
@@ -6211,16 +6317,16 @@ async function tryEnqueueCard(cardId, client, pool, config) {
|
|
|
6211
6317
|
const cardLabels = resolveCardLabels(card, labelMap);
|
|
6212
6318
|
const subtasks = card.subtasks ?? [];
|
|
6213
6319
|
if (mode === "review" && config.agent.review.approvedLabel && hasLabel(cardLabels, config.agent.review.approvedLabel)) {
|
|
6214
|
-
log.debug(
|
|
6320
|
+
log.debug(TAG29, `Card #${card.short_id} already has "${config.agent.review.approvedLabel}" — skipping review`);
|
|
6215
6321
|
return;
|
|
6216
6322
|
}
|
|
6217
6323
|
if (mode === "review" && !extractBranchFromDescription(card.description)) {
|
|
6218
|
-
log.info(
|
|
6324
|
+
log.info(TAG29, `Card #${card.short_id} has no branch reference — skipping auto-review`);
|
|
6219
6325
|
return;
|
|
6220
6326
|
}
|
|
6221
6327
|
await pool.enqueue(card, column, cardLabels, subtasks, mode);
|
|
6222
6328
|
}
|
|
6223
|
-
var
|
|
6329
|
+
var TAG29 = "daemon", PKG_VERSION;
|
|
6224
6330
|
var init_src = __esm(() => {
|
|
6225
6331
|
init_board_helpers();
|
|
6226
6332
|
init_config();
|
|
@@ -6237,6 +6343,7 @@ var init_src = __esm(() => {
|
|
|
6237
6343
|
init_state_store();
|
|
6238
6344
|
init_stream_parser_selftest();
|
|
6239
6345
|
init_types();
|
|
6346
|
+
init_unblock();
|
|
6240
6347
|
init_watcher();
|
|
6241
6348
|
init_worktree_gc();
|
|
6242
6349
|
({ version: PKG_VERSION } = createRequire2(import.meta.url)("../package.json"));
|