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