@gethmy/agent 1.7.2 → 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 +207 -90
- package/dist/index.js +207 -90
- 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();
|
|
@@ -5389,7 +5486,12 @@ function prettyBanner(config, version) {
|
|
|
5389
5486
|
gitProvider = provider;
|
|
5390
5487
|
},
|
|
5391
5488
|
check(message) {
|
|
5392
|
-
checks.push(message);
|
|
5489
|
+
checks.push({ kind: "ok", message });
|
|
5490
|
+
},
|
|
5491
|
+
warn(message) {
|
|
5492
|
+
log.warn(TAG26, message);
|
|
5493
|
+
checks.push({ kind: "warn", message: message.split(`
|
|
5494
|
+
`, 1)[0] });
|
|
5393
5495
|
},
|
|
5394
5496
|
fail() {
|
|
5395
5497
|
failed = true;
|
|
@@ -5411,19 +5513,22 @@ function prettyBanner(config, version) {
|
|
|
5411
5513
|
};
|
|
5412
5514
|
}
|
|
5413
5515
|
function jsonBanner(config, version) {
|
|
5414
|
-
log.info(
|
|
5415
|
-
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(", ")}`);
|
|
5416
5518
|
if (config.agent.review.enabled) {
|
|
5417
|
-
log.info(
|
|
5519
|
+
log.info(TAG26, `Review: enabled | Columns: ${config.agent.review.pickupColumns.join(", ")} | → ${config.agent.review.moveToColumn} / ${config.agent.review.failColumn}`);
|
|
5418
5520
|
}
|
|
5419
5521
|
let failed = false;
|
|
5420
5522
|
return {
|
|
5421
5523
|
setProjectName(_name) {},
|
|
5422
5524
|
setGitProvider(provider) {
|
|
5423
|
-
log.info(
|
|
5525
|
+
log.info(TAG26, `Git provider: ${provider}`);
|
|
5424
5526
|
},
|
|
5425
5527
|
check(message) {
|
|
5426
|
-
log.info(
|
|
5528
|
+
log.info(TAG26, message);
|
|
5529
|
+
},
|
|
5530
|
+
warn(message) {
|
|
5531
|
+
log.warn(TAG26, message);
|
|
5427
5532
|
},
|
|
5428
5533
|
fail() {
|
|
5429
5534
|
failed = true;
|
|
@@ -5431,7 +5536,7 @@ function jsonBanner(config, version) {
|
|
|
5431
5536
|
async ready(message) {
|
|
5432
5537
|
if (failed)
|
|
5433
5538
|
return;
|
|
5434
|
-
log.info(
|
|
5539
|
+
log.info(TAG26, message);
|
|
5435
5540
|
}
|
|
5436
5541
|
};
|
|
5437
5542
|
}
|
|
@@ -5445,8 +5550,8 @@ function renderPretty(input) {
|
|
|
5445
5550
|
lines.push(` ${dim(row.label.padEnd(9))} ${row.value}`);
|
|
5446
5551
|
}
|
|
5447
5552
|
lines.push("");
|
|
5448
|
-
for (const
|
|
5449
|
-
lines.push(` ${cyan("✓")} ${
|
|
5553
|
+
for (const row of checks) {
|
|
5554
|
+
lines.push(row.kind === "warn" ? ` ${yellow("⚠")} ${row.message}` : ` ${cyan("✓")} ${row.message}`);
|
|
5450
5555
|
}
|
|
5451
5556
|
lines.push("");
|
|
5452
5557
|
lines.push(`${cyan("▶")} ${cyan("Ready")} — ${readyMessage}`);
|
|
@@ -5495,13 +5600,17 @@ function dim(s) {
|
|
|
5495
5600
|
function cyan(s) {
|
|
5496
5601
|
return `${ANSI.cyan}${s}${ANSI.reset}`;
|
|
5497
5602
|
}
|
|
5498
|
-
|
|
5603
|
+
function yellow(s) {
|
|
5604
|
+
return `${ANSI.yellow}${s}${ANSI.reset}`;
|
|
5605
|
+
}
|
|
5606
|
+
var TAG26 = "daemon", RULE_WIDTH = 70, ANSI;
|
|
5499
5607
|
var init_startup_banner = __esm(() => {
|
|
5500
5608
|
init_log();
|
|
5501
5609
|
ANSI = {
|
|
5502
5610
|
reset: "\x1B[0m",
|
|
5503
5611
|
dim: "\x1B[2m",
|
|
5504
|
-
cyan: "\x1B[36m"
|
|
5612
|
+
cyan: "\x1B[36m",
|
|
5613
|
+
yellow: "\x1B[33m"
|
|
5505
5614
|
};
|
|
5506
5615
|
});
|
|
5507
5616
|
|
|
@@ -5641,18 +5750,18 @@ class Watcher {
|
|
|
5641
5750
|
}
|
|
5642
5751
|
async start() {
|
|
5643
5752
|
if (!isPretty()) {
|
|
5644
|
-
log.info(
|
|
5753
|
+
log.info(TAG27, "Connecting to Supabase realtime (broadcast)...");
|
|
5645
5754
|
}
|
|
5646
5755
|
this.supabase = createClient(this.credentials.supabaseUrl, this.credentials.supabaseAnonKey);
|
|
5647
5756
|
const presenceChannel = this.supabase.channel(`board-presence-${this.projectId}`);
|
|
5648
5757
|
const channel = this.supabase.channel(`board-${this.projectId}`).on("broadcast", { event: "card_update" }, (msg) => {
|
|
5649
|
-
log.debug(
|
|
5758
|
+
log.debug(TAG27, `Broadcast: card_update ${JSON.stringify(msg.payload)}`);
|
|
5650
5759
|
this.onCardBroadcast({
|
|
5651
5760
|
event: "card_update",
|
|
5652
5761
|
payload: msg.payload ?? {}
|
|
5653
5762
|
});
|
|
5654
5763
|
}).on("broadcast", { event: "card_created" }, (msg) => {
|
|
5655
|
-
log.debug(
|
|
5764
|
+
log.debug(TAG27, `Broadcast: card_created ${JSON.stringify(msg.payload)}`);
|
|
5656
5765
|
this.onCardBroadcast({
|
|
5657
5766
|
event: "card_created",
|
|
5658
5767
|
payload: msg.payload ?? {}
|
|
@@ -5662,29 +5771,29 @@ class Watcher {
|
|
|
5662
5771
|
const cardId = payload.card_id;
|
|
5663
5772
|
const command = payload.command;
|
|
5664
5773
|
if (cardId && command) {
|
|
5665
|
-
log.info(
|
|
5774
|
+
log.info(TAG27, `Broadcast: agent_command ${command} for ${cardId}`);
|
|
5666
5775
|
this.onAgentCommand?.({ cardId, command });
|
|
5667
5776
|
}
|
|
5668
5777
|
}).subscribe((status) => {
|
|
5669
5778
|
if (status === "SUBSCRIBED") {
|
|
5670
5779
|
this.connected = true;
|
|
5671
5780
|
if (!isPretty() || !this.suppressStartupLogs) {
|
|
5672
|
-
log.info(
|
|
5781
|
+
log.info(TAG27, "Broadcast subscription active");
|
|
5673
5782
|
}
|
|
5674
5783
|
this.maybeResolveReady();
|
|
5675
5784
|
} else if (status === "CHANNEL_ERROR") {
|
|
5676
5785
|
this.connected = false;
|
|
5677
|
-
log.error(
|
|
5786
|
+
log.error(TAG27, "Broadcast channel error — will rely on reconciliation");
|
|
5678
5787
|
} else if (status === "TIMED_OUT") {
|
|
5679
5788
|
this.connected = false;
|
|
5680
|
-
log.warn(
|
|
5789
|
+
log.warn(TAG27, "Broadcast subscription timed out — retrying...");
|
|
5681
5790
|
} else if (status === "CLOSED") {
|
|
5682
5791
|
this.connected = false;
|
|
5683
5792
|
}
|
|
5684
5793
|
});
|
|
5685
5794
|
this.channel = channel;
|
|
5686
5795
|
presenceChannel.on("presence", { event: "sync" }, () => {
|
|
5687
|
-
log.debug(
|
|
5796
|
+
log.debug(TAG27, "Presence sync");
|
|
5688
5797
|
}).subscribe(async (status) => {
|
|
5689
5798
|
if (status === "SUBSCRIBED") {
|
|
5690
5799
|
await presenceChannel.track({
|
|
@@ -5696,7 +5805,7 @@ class Watcher {
|
|
|
5696
5805
|
agentName: this.identity.agentName
|
|
5697
5806
|
});
|
|
5698
5807
|
if (!isPretty() || !this.suppressStartupLogs) {
|
|
5699
|
-
log.info(
|
|
5808
|
+
log.info(TAG27, "Presence tracked on board-presence channel");
|
|
5700
5809
|
}
|
|
5701
5810
|
this.presenceTracked = true;
|
|
5702
5811
|
this.maybeResolveReady();
|
|
@@ -5718,10 +5827,10 @@ class Watcher {
|
|
|
5718
5827
|
this.supabase = null;
|
|
5719
5828
|
}
|
|
5720
5829
|
this.connected = false;
|
|
5721
|
-
log.info(
|
|
5830
|
+
log.info(TAG27, "Broadcast subscription stopped");
|
|
5722
5831
|
}
|
|
5723
5832
|
}
|
|
5724
|
-
var
|
|
5833
|
+
var TAG27 = "watcher";
|
|
5725
5834
|
var init_watcher = __esm(() => {
|
|
5726
5835
|
init_log();
|
|
5727
5836
|
});
|
|
@@ -5796,10 +5905,10 @@ function runWorktreeGc(basePath, store, opts = {}) {
|
|
|
5796
5905
|
});
|
|
5797
5906
|
} catch {}
|
|
5798
5907
|
if (result.removed.length > 0) {
|
|
5799
|
-
log.info(
|
|
5908
|
+
log.info(TAG28, `GC removed ${result.removed.length} orphan worktree(s): ${result.removed.map((p) => p.split("/").pop()).join(", ")}`);
|
|
5800
5909
|
}
|
|
5801
5910
|
if (result.errors.length > 0) {
|
|
5802
|
-
log.warn(
|
|
5911
|
+
log.warn(TAG28, `GC had ${result.errors.length} error(s): ${result.errors.map((e) => `${e.path}: ${e.error}`).join("; ")}`);
|
|
5803
5912
|
}
|
|
5804
5913
|
return result;
|
|
5805
5914
|
}
|
|
@@ -5876,10 +5985,10 @@ function pruneFailedRemoteBranches(opts) {
|
|
|
5876
5985
|
}
|
|
5877
5986
|
}
|
|
5878
5987
|
if (result.removed.length > 0) {
|
|
5879
|
-
log.info(
|
|
5988
|
+
log.info(TAG28, `Pruned ${result.removed.length} stale remote branch(es) under ${opts.prefix}: ${result.removed.join(", ")}`);
|
|
5880
5989
|
}
|
|
5881
5990
|
if (result.errors.length > 0) {
|
|
5882
|
-
log.warn(
|
|
5991
|
+
log.warn(TAG28, `Remote branch GC had ${result.errors.length} error(s): ${result.errors.map((e) => `${e.ref}: ${e.error}`).join("; ")}`);
|
|
5883
5992
|
}
|
|
5884
5993
|
return result;
|
|
5885
5994
|
}
|
|
@@ -5910,13 +6019,13 @@ class WorktreeGc {
|
|
|
5910
6019
|
try {
|
|
5911
6020
|
runWorktreeGc(this.basePath, this.store);
|
|
5912
6021
|
} catch (err) {
|
|
5913
|
-
log.warn(
|
|
6022
|
+
log.warn(TAG28, `GC tick failed: ${err instanceof Error ? err.message : err}`);
|
|
5914
6023
|
}
|
|
5915
6024
|
if (this.remoteOpts) {
|
|
5916
6025
|
try {
|
|
5917
6026
|
pruneFailedRemoteBranches(this.remoteOpts);
|
|
5918
6027
|
} catch (err) {
|
|
5919
|
-
log.warn(
|
|
6028
|
+
log.warn(TAG28, `Remote GC tick failed: ${err instanceof Error ? err.message : err}`);
|
|
5920
6029
|
}
|
|
5921
6030
|
}
|
|
5922
6031
|
}
|
|
@@ -5930,7 +6039,7 @@ function getRepoRoot2() {
|
|
|
5930
6039
|
return null;
|
|
5931
6040
|
}
|
|
5932
6041
|
}
|
|
5933
|
-
var
|
|
6042
|
+
var TAG28 = "worktree-gc";
|
|
5934
6043
|
var init_worktree_gc = __esm(() => {
|
|
5935
6044
|
init_log();
|
|
5936
6045
|
init_worktree();
|
|
@@ -5966,8 +6075,7 @@ async function validatePrerequisites(config, banner) {
|
|
|
5966
6075
|
encoding: "utf-8"
|
|
5967
6076
|
}).trim();
|
|
5968
6077
|
if (status) {
|
|
5969
|
-
banner.
|
|
5970
|
-
log.warn(TAG28, `Working directory has uncommitted changes:
|
|
6078
|
+
banner.warn(`Working directory has uncommitted changes:
|
|
5971
6079
|
${status}`);
|
|
5972
6080
|
}
|
|
5973
6081
|
execFileSync10("git", ["rev-parse", "--verify", `origin/${config.agent.worktree.baseBranch}`], {
|
|
@@ -6013,7 +6121,7 @@ async function main() {
|
|
|
6013
6121
|
} catch (err) {
|
|
6014
6122
|
if (err instanceof ConfigValidationError) {
|
|
6015
6123
|
banner.fail();
|
|
6016
|
-
log.error(
|
|
6124
|
+
log.error(TAG29, err.message);
|
|
6017
6125
|
process.exit(1);
|
|
6018
6126
|
}
|
|
6019
6127
|
throw err;
|
|
@@ -6031,12 +6139,21 @@ async function main() {
|
|
|
6031
6139
|
const realtimeCreds = await fetchRealtimeCredentials(client);
|
|
6032
6140
|
banner.check("Realtime credentials");
|
|
6033
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;
|
|
6034
6150
|
const reviewColumns = config.agent.review.enabled ? config.agent.review.pickupColumns : [];
|
|
6035
6151
|
const approvedLabel = config.agent.review.enabled ? config.agent.review.approvedLabel : "";
|
|
6036
6152
|
const reconciler = new Reconciler(client, pool, config.projectId, agentUserId, config.agent.pickupColumns, reviewColumns, approvedLabel, config.agent.timing.reconcileIntervalMs, stateStore, config.agent);
|
|
6037
6153
|
let mergeMonitor = null;
|
|
6038
6154
|
if (config.agent.review.enabled && config.agent.review.mergeMonitor) {
|
|
6039
6155
|
mergeMonitor = new MergeMonitor(client, config.projectId, config.agent);
|
|
6156
|
+
mergeMonitor.onCardCompleted = promoteSuccessors;
|
|
6040
6157
|
}
|
|
6041
6158
|
const worktreeGc = new WorktreeGc(config.agent.worktree.basePath, stateStore, config.agent.timing.worktreeGcIntervalMs, config.agent.worktree.failedAttemptRetentionDays > 0 ? {
|
|
6042
6159
|
prefix: config.agent.worktree.failedBranchPrefix,
|
|
@@ -6105,25 +6222,25 @@ async function main() {
|
|
|
6105
6222
|
if (shuttingDown)
|
|
6106
6223
|
return;
|
|
6107
6224
|
shuttingDown = true;
|
|
6108
|
-
log.info(
|
|
6225
|
+
log.info(TAG29, `Received ${signal}, shutting down gracefully...`);
|
|
6109
6226
|
reconciler.stop();
|
|
6110
6227
|
mergeMonitor?.stop();
|
|
6111
6228
|
worktreeGc.stop();
|
|
6112
6229
|
await httpServer?.stop();
|
|
6113
6230
|
await watcher.stop();
|
|
6114
6231
|
await pool.shutdown();
|
|
6115
|
-
log.info(
|
|
6232
|
+
log.info(TAG29, "Daemon stopped.");
|
|
6116
6233
|
process.exit(exitCode);
|
|
6117
6234
|
};
|
|
6118
6235
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
6119
6236
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
6120
6237
|
process.on("uncaughtException", (err) => {
|
|
6121
|
-
log.error(
|
|
6238
|
+
log.error(TAG29, `Uncaught exception: ${err.message}`);
|
|
6122
6239
|
exitCode = 1;
|
|
6123
6240
|
shutdown("uncaughtException");
|
|
6124
6241
|
});
|
|
6125
6242
|
process.on("unhandledRejection", (reason) => {
|
|
6126
|
-
log.error(
|
|
6243
|
+
log.error(TAG29, `Unhandled rejection: ${reason instanceof Error ? reason.message : String(reason)}`);
|
|
6127
6244
|
exitCode = 1;
|
|
6128
6245
|
shutdown("unhandledRejection");
|
|
6129
6246
|
});
|
|
@@ -6135,8 +6252,7 @@ async function main() {
|
|
|
6135
6252
|
try {
|
|
6136
6253
|
await httpServer.start();
|
|
6137
6254
|
} catch (err) {
|
|
6138
|
-
banner.
|
|
6139
|
-
log.warn(TAG28, `HTTP server failed to bind: ${err instanceof Error ? err.message : err}`);
|
|
6255
|
+
banner.warn(`HTTP server failed to bind: ${err instanceof Error ? err.message : err}`);
|
|
6140
6256
|
}
|
|
6141
6257
|
}
|
|
6142
6258
|
const reviewCount = config.agent.review.enabled ? config.agent.review.poolSize : 0;
|
|
@@ -6171,14 +6287,14 @@ async function handleBroadcast(event, client, pool, config, agentUserId) {
|
|
|
6171
6287
|
if (assigneeId === undefined)
|
|
6172
6288
|
return;
|
|
6173
6289
|
if (assigneeId === agentUserId) {
|
|
6174
|
-
log.info(
|
|
6290
|
+
log.info(TAG29, `Broadcast: card ${cardId} assigned to agent`);
|
|
6175
6291
|
try {
|
|
6176
6292
|
await tryEnqueueCard(cardId, client, pool, config);
|
|
6177
6293
|
} catch (err) {
|
|
6178
|
-
log.error(
|
|
6294
|
+
log.error(TAG29, `Failed to process assignment: ${err instanceof Error ? err.message : err}`);
|
|
6179
6295
|
}
|
|
6180
6296
|
} else if (pool.isCardKnown(cardId)) {
|
|
6181
|
-
log.info(
|
|
6297
|
+
log.info(TAG29, `Broadcast: card ${cardId} unassigned from agent`);
|
|
6182
6298
|
await pool.removeCard(cardId);
|
|
6183
6299
|
}
|
|
6184
6300
|
}
|
|
@@ -6188,13 +6304,13 @@ async function tryEnqueueCard(cardId, client, pool, config) {
|
|
|
6188
6304
|
const columns = board.columns;
|
|
6189
6305
|
const column = columns.find((c) => c.id === card.column_id);
|
|
6190
6306
|
if (!column) {
|
|
6191
|
-
log.warn(
|
|
6307
|
+
log.warn(TAG29, `Column not found for card ${cardId}`);
|
|
6192
6308
|
return;
|
|
6193
6309
|
}
|
|
6194
6310
|
const isPickupColumn = config.agent.pickupColumns.some((name) => name.toLowerCase() === column.name.toLowerCase());
|
|
6195
6311
|
const isReviewColumn = config.agent.review.enabled && config.agent.review.pickupColumns.some((name) => name.toLowerCase() === column.name.toLowerCase());
|
|
6196
6312
|
if (!isPickupColumn && !isReviewColumn) {
|
|
6197
|
-
log.info(
|
|
6313
|
+
log.info(TAG29, `Card #${card.short_id} is in "${column.name}", not a pickup/review column — skipping`);
|
|
6198
6314
|
return;
|
|
6199
6315
|
}
|
|
6200
6316
|
const mode = isReviewColumn ? "review" : "implement";
|
|
@@ -6202,16 +6318,16 @@ async function tryEnqueueCard(cardId, client, pool, config) {
|
|
|
6202
6318
|
const cardLabels = resolveCardLabels(card, labelMap);
|
|
6203
6319
|
const subtasks = card.subtasks ?? [];
|
|
6204
6320
|
if (mode === "review" && config.agent.review.approvedLabel && hasLabel(cardLabels, config.agent.review.approvedLabel)) {
|
|
6205
|
-
log.debug(
|
|
6321
|
+
log.debug(TAG29, `Card #${card.short_id} already has "${config.agent.review.approvedLabel}" — skipping review`);
|
|
6206
6322
|
return;
|
|
6207
6323
|
}
|
|
6208
6324
|
if (mode === "review" && !extractBranchFromDescription(card.description)) {
|
|
6209
|
-
log.info(
|
|
6325
|
+
log.info(TAG29, `Card #${card.short_id} has no branch reference — skipping auto-review`);
|
|
6210
6326
|
return;
|
|
6211
6327
|
}
|
|
6212
6328
|
await pool.enqueue(card, column, cardLabels, subtasks, mode);
|
|
6213
6329
|
}
|
|
6214
|
-
var
|
|
6330
|
+
var TAG29 = "daemon", PKG_VERSION;
|
|
6215
6331
|
var init_src = __esm(() => {
|
|
6216
6332
|
init_board_helpers();
|
|
6217
6333
|
init_config();
|
|
@@ -6228,6 +6344,7 @@ var init_src = __esm(() => {
|
|
|
6228
6344
|
init_state_store();
|
|
6229
6345
|
init_stream_parser_selftest();
|
|
6230
6346
|
init_types();
|
|
6347
|
+
init_unblock();
|
|
6231
6348
|
init_watcher();
|
|
6232
6349
|
init_worktree_gc();
|
|
6233
6350
|
({ version: PKG_VERSION } = createRequire2(import.meta.url)("../package.json"));
|