@gethmy/agent 1.8.0 → 1.8.1
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 +187 -90
- package/dist/index.js +187 -90
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3755,6 +3755,7 @@ class ReviewWorker {
|
|
|
3755
3755
|
progressTracker = null;
|
|
3756
3756
|
lastSessionStats = null;
|
|
3757
3757
|
aborted = false;
|
|
3758
|
+
timedOut = false;
|
|
3758
3759
|
runId = null;
|
|
3759
3760
|
lastRunLogPath = null;
|
|
3760
3761
|
sessionId = null;
|
|
@@ -3810,6 +3811,7 @@ class ReviewWorker {
|
|
|
3810
3811
|
}
|
|
3811
3812
|
async run(card, column, labels, subtasks) {
|
|
3812
3813
|
this.aborted = false;
|
|
3814
|
+
this.timedOut = false;
|
|
3813
3815
|
this.cardId = card.id;
|
|
3814
3816
|
this.startedAt = Date.now();
|
|
3815
3817
|
this.runId = newRunId();
|
|
@@ -3947,6 +3949,7 @@ class ReviewWorker {
|
|
|
3947
3949
|
});
|
|
3948
3950
|
this.timeoutTimer = setTimeout(() => {
|
|
3949
3951
|
log.warn(this.tag, `Review timeout reached (${this.config.review.maxTimeout}ms), cancelling`);
|
|
3952
|
+
this.timedOut = true;
|
|
3950
3953
|
this.cancel();
|
|
3951
3954
|
}, this.config.review.maxTimeout);
|
|
3952
3955
|
this.progressTracker = new ProgressTracker(this.client, card.id, this.id, subtasks);
|
|
@@ -4013,7 +4016,7 @@ class ReviewWorker {
|
|
|
4013
4016
|
try {
|
|
4014
4017
|
const run = this.stateStore.getRun(this.runId);
|
|
4015
4018
|
if (run && run.endedAt === null) {
|
|
4016
|
-
const status = this.state === "error" || this.aborted ? "paused" : "completed";
|
|
4019
|
+
const status = this.timedOut ? "failed" : this.state === "error" || this.aborted ? "paused" : "completed";
|
|
4017
4020
|
await this.stateStore.endRun(this.runId, status);
|
|
4018
4021
|
}
|
|
4019
4022
|
} catch {}
|
|
@@ -4051,6 +4054,7 @@ class ReviewWorker {
|
|
|
4051
4054
|
signalGroup(this.process, "SIGCONT");
|
|
4052
4055
|
this.timeoutTimer = setTimeout(() => {
|
|
4053
4056
|
log.warn(this.tag, `Timeout reached (${this.config.review.maxTimeout}ms), cancelling`);
|
|
4057
|
+
this.timedOut = true;
|
|
4054
4058
|
this.cancel();
|
|
4055
4059
|
}, this.config.review.maxTimeout);
|
|
4056
4060
|
if (this.cardId) {
|
|
@@ -4086,7 +4090,11 @@ class ReviewWorker {
|
|
|
4086
4090
|
if (this.cardId) {
|
|
4087
4091
|
try {
|
|
4088
4092
|
await this.client.endAgentSession(this.cardId, {
|
|
4089
|
-
status: "paused",
|
|
4093
|
+
status: this.timedOut ? "failed" : "paused",
|
|
4094
|
+
...this.timedOut ? {
|
|
4095
|
+
failureReason: "timeout",
|
|
4096
|
+
failureSummary: `Review exceeded the ${Math.round(this.config.review.maxTimeout / 60000)} min timeout`
|
|
4097
|
+
} : {},
|
|
4090
4098
|
...buildTokenPayload(snapshotStats)
|
|
4091
4099
|
});
|
|
4092
4100
|
} catch {}
|
|
@@ -4264,13 +4272,74 @@ var init_review_worker = __esm(() => {
|
|
|
4264
4272
|
init_worktree();
|
|
4265
4273
|
});
|
|
4266
4274
|
|
|
4275
|
+
// src/sleep-guard.ts
|
|
4276
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
4277
|
+
|
|
4278
|
+
class SleepGuard {
|
|
4279
|
+
platform;
|
|
4280
|
+
child = null;
|
|
4281
|
+
holds = 0;
|
|
4282
|
+
constructor(platform = process.platform) {
|
|
4283
|
+
this.platform = platform;
|
|
4284
|
+
}
|
|
4285
|
+
get active() {
|
|
4286
|
+
return this.child !== null;
|
|
4287
|
+
}
|
|
4288
|
+
acquire() {
|
|
4289
|
+
this.holds++;
|
|
4290
|
+
if (this.holds === 1)
|
|
4291
|
+
this.start();
|
|
4292
|
+
}
|
|
4293
|
+
release() {
|
|
4294
|
+
this.holds = Math.max(0, this.holds - 1);
|
|
4295
|
+
if (this.holds === 0)
|
|
4296
|
+
this.stop();
|
|
4297
|
+
}
|
|
4298
|
+
stop() {
|
|
4299
|
+
this.holds = 0;
|
|
4300
|
+
if (this.child) {
|
|
4301
|
+
if (!this.child.killed)
|
|
4302
|
+
this.child.kill("SIGTERM");
|
|
4303
|
+
this.child = null;
|
|
4304
|
+
log.info(TAG20, "sleep assertion released");
|
|
4305
|
+
}
|
|
4306
|
+
}
|
|
4307
|
+
start() {
|
|
4308
|
+
if (this.platform !== "darwin" || this.child)
|
|
4309
|
+
return;
|
|
4310
|
+
try {
|
|
4311
|
+
const child = spawn3("caffeinate", ["-i", "-w", String(process.pid)], {
|
|
4312
|
+
stdio: "ignore"
|
|
4313
|
+
});
|
|
4314
|
+
child.on("error", (err) => {
|
|
4315
|
+
log.warn(TAG20, `caffeinate unavailable: ${err.message}`);
|
|
4316
|
+
if (this.child === child)
|
|
4317
|
+
this.child = null;
|
|
4318
|
+
});
|
|
4319
|
+
child.on("exit", () => {
|
|
4320
|
+
if (this.child === child)
|
|
4321
|
+
this.child = null;
|
|
4322
|
+
});
|
|
4323
|
+
child.unref();
|
|
4324
|
+
this.child = child;
|
|
4325
|
+
log.info(TAG20, "sleep assertion acquired (caffeinate -i)");
|
|
4326
|
+
} catch (err) {
|
|
4327
|
+
log.warn(TAG20, `failed to spawn caffeinate: ${err instanceof Error ? err.message : err}`);
|
|
4328
|
+
}
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
var TAG20 = "sleep-guard";
|
|
4332
|
+
var init_sleep_guard = __esm(() => {
|
|
4333
|
+
init_log();
|
|
4334
|
+
});
|
|
4335
|
+
|
|
4267
4336
|
// src/unblock.ts
|
|
4268
4337
|
async function fetchBlocksLinks(client, cardId) {
|
|
4269
4338
|
try {
|
|
4270
4339
|
const { links } = await client.getCardLinks(cardId);
|
|
4271
4340
|
return links.filter((l) => l.link_type === "blocks");
|
|
4272
4341
|
} catch (err) {
|
|
4273
|
-
log.warn(
|
|
4342
|
+
log.warn(TAG21, `link fetch failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
|
|
4274
4343
|
return null;
|
|
4275
4344
|
}
|
|
4276
4345
|
}
|
|
@@ -4302,22 +4371,22 @@ async function promoteUnblockedSuccessors(completedCard, deps) {
|
|
|
4302
4371
|
const successors = links.filter((l) => l.direction === "outgoing" && !l.target_card.done);
|
|
4303
4372
|
if (successors.length === 0)
|
|
4304
4373
|
return;
|
|
4305
|
-
log.info(
|
|
4374
|
+
log.info(TAG21, `#${completedCard.short_id} completed — checking ${successors.length} chained successor(s)`);
|
|
4306
4375
|
for (const link of successors) {
|
|
4307
4376
|
const successorId = link.target_card.id;
|
|
4308
4377
|
try {
|
|
4309
4378
|
const { card } = await deps.client.getCard(successorId);
|
|
4310
4379
|
if (card.assignee_id !== deps.agentUserId) {
|
|
4311
|
-
log.debug(
|
|
4380
|
+
log.debug(TAG21, `successor #${card.short_id} not assigned to agent — skipping promotion`);
|
|
4312
4381
|
continue;
|
|
4313
4382
|
}
|
|
4314
4383
|
await deps.enqueue(successorId);
|
|
4315
4384
|
} catch (err) {
|
|
4316
|
-
log.warn(
|
|
4385
|
+
log.warn(TAG21, `promotion failed for successor ${successorId}: ${err instanceof Error ? err.message : err}`);
|
|
4317
4386
|
}
|
|
4318
4387
|
}
|
|
4319
4388
|
}
|
|
4320
|
-
var
|
|
4389
|
+
var TAG21 = "unblock";
|
|
4321
4390
|
var init_unblock = __esm(() => {
|
|
4322
4391
|
init_log();
|
|
4323
4392
|
});
|
|
@@ -4461,11 +4530,11 @@ async function buildPrompt(enriched, branchName, worktreePath, client, workspace
|
|
|
4461
4530
|
Do NOT push to main. All your work stays on \`${branchName}\`.
|
|
4462
4531
|
When finished, call harmony_end_agent_session with status="completed".`
|
|
4463
4532
|
});
|
|
4464
|
-
log.info(
|
|
4533
|
+
log.info(TAG22, `Generated prompt for #${card.short_id} — ${result.contextSummary.memoryCount} memories, ${result.tokenEstimate} tokens`);
|
|
4465
4534
|
return result.prompt + pastEpisodesSection;
|
|
4466
4535
|
} catch (err) {
|
|
4467
4536
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4468
|
-
log.warn(
|
|
4537
|
+
log.warn(TAG22, `Failed to generate prompt via API, using fallback: ${msg}`);
|
|
4469
4538
|
const commentsSection = await renderCommentsSection(client, card.id);
|
|
4470
4539
|
return buildFallbackPrompt(enriched, branchName, worktreePath) + commentsSection + pastEpisodesSection;
|
|
4471
4540
|
}
|
|
@@ -4483,7 +4552,7 @@ async function renderCommentsSection(client, cardId) {
|
|
|
4483
4552
|
|
|
4484
4553
|
${section}` : "";
|
|
4485
4554
|
} catch (err) {
|
|
4486
|
-
log.warn(
|
|
4555
|
+
log.warn(TAG22, "comment-thread fetch failed", {
|
|
4487
4556
|
event: "comment_fetch_failed",
|
|
4488
4557
|
error: err instanceof Error ? err.message : String(err)
|
|
4489
4558
|
});
|
|
@@ -4533,7 +4602,7 @@ ${description}`.trim();
|
|
|
4533
4602
|
## Similar past tasks
|
|
4534
4603
|
${bullets}`;
|
|
4535
4604
|
} catch (err) {
|
|
4536
|
-
log.warn(
|
|
4605
|
+
log.warn(TAG22, "past-episodes recall failed", {
|
|
4537
4606
|
event: "episode_recall_failed",
|
|
4538
4607
|
error: err instanceof Error ? err.message : String(err)
|
|
4539
4608
|
});
|
|
@@ -4574,7 +4643,7 @@ ${subtaskStr}
|
|
|
4574
4643
|
You are working in a git worktree at \`${worktreePath}\` on branch \`${branchName}\`.
|
|
4575
4644
|
Do NOT push to main. All your work stays on \`${branchName}\`.`;
|
|
4576
4645
|
}
|
|
4577
|
-
var
|
|
4646
|
+
var TAG22 = "prompt";
|
|
4578
4647
|
var init_prompt = __esm(() => {
|
|
4579
4648
|
init_dist();
|
|
4580
4649
|
init_log();
|
|
@@ -4601,6 +4670,7 @@ class Worker {
|
|
|
4601
4670
|
progressTracker = null;
|
|
4602
4671
|
lastSessionStats;
|
|
4603
4672
|
aborted = false;
|
|
4673
|
+
timedOut = false;
|
|
4604
4674
|
verificationFailed = false;
|
|
4605
4675
|
sessionId = null;
|
|
4606
4676
|
runId = null;
|
|
@@ -4646,7 +4716,7 @@ class Worker {
|
|
|
4646
4716
|
}
|
|
4647
4717
|
}
|
|
4648
4718
|
get tag() {
|
|
4649
|
-
return `${
|
|
4719
|
+
return `${TAG23}:${this.id}`;
|
|
4650
4720
|
}
|
|
4651
4721
|
get isIdle() {
|
|
4652
4722
|
return this.state === "idle";
|
|
@@ -4656,6 +4726,7 @@ class Worker {
|
|
|
4656
4726
|
}
|
|
4657
4727
|
async run(card, column, labels, subtasks) {
|
|
4658
4728
|
this.aborted = false;
|
|
4729
|
+
this.timedOut = false;
|
|
4659
4730
|
this.verificationFailed = false;
|
|
4660
4731
|
this.cardId = card.id;
|
|
4661
4732
|
this.startedAt = Date.now();
|
|
@@ -4692,7 +4763,7 @@ class Worker {
|
|
|
4692
4763
|
});
|
|
4693
4764
|
const sid = session && typeof session === "object" && "id" in session ? session.id : null;
|
|
4694
4765
|
if (!sid) {
|
|
4695
|
-
log.warn(
|
|
4766
|
+
log.warn(TAG23, "startAgentSession returned no session id");
|
|
4696
4767
|
}
|
|
4697
4768
|
this.sessionId = sid;
|
|
4698
4769
|
await this.recordPhase("preparing");
|
|
@@ -4724,6 +4795,7 @@ class Worker {
|
|
|
4724
4795
|
});
|
|
4725
4796
|
this.timeoutTimer = setTimeout(() => {
|
|
4726
4797
|
log.warn(this.tag, `Timeout reached (${this.config.maxTimeout}ms), cancelling`);
|
|
4798
|
+
this.timedOut = true;
|
|
4727
4799
|
this.cancel();
|
|
4728
4800
|
}, this.config.maxTimeout);
|
|
4729
4801
|
await this.spawnClaude(prompt, card, subtasks);
|
|
@@ -4770,6 +4842,24 @@ class Worker {
|
|
|
4770
4842
|
await this.stateStore.endRun(this.runId, "completed");
|
|
4771
4843
|
} catch {}
|
|
4772
4844
|
await this.recordOutcome(card.id, "success");
|
|
4845
|
+
} else if (this.runId && this.timedOut) {
|
|
4846
|
+
try {
|
|
4847
|
+
await runTransition(this.client, card, {
|
|
4848
|
+
move: { columnName: this.config.pickupColumns[0] ?? "To Do" },
|
|
4849
|
+
endSession: {
|
|
4850
|
+
status: "failed",
|
|
4851
|
+
failureReason: "timeout",
|
|
4852
|
+
failureSummary: `Run exceeded the ${Math.round(this.config.maxTimeout / 60000)} min timeout`,
|
|
4853
|
+
...buildTokenPayload(this.lastSessionStats)
|
|
4854
|
+
}
|
|
4855
|
+
});
|
|
4856
|
+
} catch (tErr) {
|
|
4857
|
+
log.error(this.tag, `timeout transition failed on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
|
|
4858
|
+
}
|
|
4859
|
+
try {
|
|
4860
|
+
await this.stateStore.endRun(this.runId, "failed", "timeout");
|
|
4861
|
+
} catch {}
|
|
4862
|
+
await this.recordOutcome(card.id, "failure");
|
|
4773
4863
|
} else if (this.runId && this.aborted) {
|
|
4774
4864
|
try {
|
|
4775
4865
|
await this.stateStore.endRun(this.runId, "paused", "cancelled");
|
|
@@ -4840,6 +4930,7 @@ class Worker {
|
|
|
4840
4930
|
signalGroup(this.process, "SIGCONT");
|
|
4841
4931
|
this.timeoutTimer = setTimeout(() => {
|
|
4842
4932
|
log.warn(this.tag, `Timeout reached (${this.config.maxTimeout}ms), cancelling`);
|
|
4933
|
+
this.timedOut = true;
|
|
4843
4934
|
this.cancel();
|
|
4844
4935
|
}, this.config.maxTimeout);
|
|
4845
4936
|
if (this.cardId) {
|
|
@@ -4866,7 +4957,7 @@ class Worker {
|
|
|
4866
4957
|
sigtermTimeoutMs: CANCEL_SIGTERM_TIMEOUT2
|
|
4867
4958
|
});
|
|
4868
4959
|
}
|
|
4869
|
-
if (this.cardId) {
|
|
4960
|
+
if (this.cardId && !this.timedOut) {
|
|
4870
4961
|
try {
|
|
4871
4962
|
const stats = this.lastSessionStats ?? this.progressTracker?.stats;
|
|
4872
4963
|
await this.client.endAgentSession(this.cardId, {
|
|
@@ -4970,7 +5061,7 @@ class Worker {
|
|
|
4970
5061
|
clearTimeout(this.timeoutTimer);
|
|
4971
5062
|
this.timeoutTimer = null;
|
|
4972
5063
|
}
|
|
4973
|
-
if (this.worktreePath && this.state === "error") {
|
|
5064
|
+
if (this.worktreePath && (this.state === "error" || this.timedOut)) {
|
|
4974
5065
|
try {
|
|
4975
5066
|
cleanupWorktree(this.worktreePath, this.branchName ?? undefined);
|
|
4976
5067
|
} catch {
|
|
@@ -4986,7 +5077,7 @@ class Worker {
|
|
|
4986
5077
|
this.sessionId = null;
|
|
4987
5078
|
}
|
|
4988
5079
|
}
|
|
4989
|
-
var
|
|
5080
|
+
var TAG23 = "worker", CANCEL_SIGINT_TIMEOUT2 = 30000, CANCEL_SIGTERM_TIMEOUT2 = 1e4;
|
|
4990
5081
|
var init_worker = __esm(() => {
|
|
4991
5082
|
init_board_helpers();
|
|
4992
5083
|
init_completion();
|
|
@@ -5012,6 +5103,7 @@ class Pool {
|
|
|
5012
5103
|
implQueue;
|
|
5013
5104
|
reviewQueue;
|
|
5014
5105
|
budget;
|
|
5106
|
+
sleepGuard = new SleepGuard;
|
|
5015
5107
|
onCardCompleted = null;
|
|
5016
5108
|
constructor(config, client, userEmail, workspaceId, projectId, stateStore) {
|
|
5017
5109
|
this.client = client;
|
|
@@ -5023,6 +5115,7 @@ class Pool {
|
|
|
5023
5115
|
for (let i = 0;i < config.poolSize; i++) {
|
|
5024
5116
|
this.implWorkers.push(new Worker(i, config, client, userEmail, () => {
|
|
5025
5117
|
this.tryDispatchFor(this.implWorkers, this.implQueue, "impl");
|
|
5118
|
+
this.sleepGuard.release();
|
|
5026
5119
|
}, workspaceId, projectId, stateStore, async (completedCard) => {
|
|
5027
5120
|
await this.onCardCompleted?.(completedCard);
|
|
5028
5121
|
}));
|
|
@@ -5032,34 +5125,35 @@ class Pool {
|
|
|
5032
5125
|
const reviewWorkerId = config.poolSize + i;
|
|
5033
5126
|
this.reviewWorkers.push(new ReviewWorker(reviewWorkerId, config, client, userEmail, () => {
|
|
5034
5127
|
this.tryDispatchFor(this.reviewWorkers, this.reviewQueue, "review");
|
|
5128
|
+
this.sleepGuard.release();
|
|
5035
5129
|
}, stateStore, workspaceId, projectId));
|
|
5036
5130
|
}
|
|
5037
5131
|
}
|
|
5038
5132
|
}
|
|
5039
5133
|
async enqueue(card, column, labels, subtasks, mode = "implement") {
|
|
5040
5134
|
if (this.implQueue.has(card.id) || this.reviewQueue.has(card.id) || this.isCardActive(card.id)) {
|
|
5041
|
-
log.debug(
|
|
5135
|
+
log.debug(TAG24, `Card ${card.id} already queued or active, skipping`);
|
|
5042
5136
|
return;
|
|
5043
5137
|
}
|
|
5044
5138
|
if (mode === "implement") {
|
|
5045
5139
|
const decision = this.budget.check(card.id);
|
|
5046
5140
|
if (!decision.allow) {
|
|
5047
5141
|
if (decision.reason === "daily_budget") {
|
|
5048
|
-
log.warn(
|
|
5142
|
+
log.warn(TAG24, `#${card.short_id} skipped (daily_budget): ${decision.detail}`);
|
|
5049
5143
|
await this.emitWaiting(card.id, `Daily budget reached — waiting for reset (${decision.detail})`);
|
|
5050
5144
|
} else {
|
|
5051
|
-
log.debug(
|
|
5145
|
+
log.debug(TAG24, `#${card.short_id} gave up: ${decision.detail}`);
|
|
5052
5146
|
}
|
|
5053
5147
|
return;
|
|
5054
5148
|
}
|
|
5055
5149
|
const blockers = await getUnresolvedBlockers(this.client, card, this.projectId);
|
|
5056
5150
|
if (blockers === null) {
|
|
5057
|
-
log.warn(
|
|
5151
|
+
log.warn(TAG24, `#${card.short_id} blocker check failed — deferring to next tick`);
|
|
5058
5152
|
return;
|
|
5059
5153
|
}
|
|
5060
5154
|
if (blockers.length > 0) {
|
|
5061
5155
|
const list = blockers.map((b) => `#${b.shortId}`).join(", ");
|
|
5062
|
-
log.info(
|
|
5156
|
+
log.info(TAG24, `#${card.short_id} blocked by ${list} — waiting`);
|
|
5063
5157
|
await this.emitWaiting(card.id, `Blocked by ${list} — waiting for chain`);
|
|
5064
5158
|
return;
|
|
5065
5159
|
}
|
|
@@ -5088,7 +5182,7 @@ class Pool {
|
|
|
5088
5182
|
});
|
|
5089
5183
|
this.lastWaitingEmit.set(cardId, currentTask);
|
|
5090
5184
|
} catch (err) {
|
|
5091
|
-
log.debug(
|
|
5185
|
+
log.debug(TAG24, `waiting emit failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
|
|
5092
5186
|
}
|
|
5093
5187
|
}
|
|
5094
5188
|
async removeCard(cardId) {
|
|
@@ -5098,13 +5192,13 @@ class Pool {
|
|
|
5098
5192
|
const removed = queue.remove(cardId);
|
|
5099
5193
|
if (removed) {
|
|
5100
5194
|
this.cardDataCache.delete(cardId);
|
|
5101
|
-
log.info(
|
|
5195
|
+
log.info(TAG24, `Removed #${removed.shortId} from ${removed.mode} queue`);
|
|
5102
5196
|
return;
|
|
5103
5197
|
}
|
|
5104
5198
|
}
|
|
5105
5199
|
const worker = this.implWorkers.find((w) => w.cardId === cardId) ?? this.reviewWorkers.find((w) => w.cardId === cardId);
|
|
5106
5200
|
if (worker) {
|
|
5107
|
-
log.info(
|
|
5201
|
+
log.info(TAG24, `Cancelling worker ${worker.id} for card ${cardId}`);
|
|
5108
5202
|
await worker.cancel();
|
|
5109
5203
|
}
|
|
5110
5204
|
}
|
|
@@ -5132,10 +5226,10 @@ class Pool {
|
|
|
5132
5226
|
async handleAgentCommand(cardId, command) {
|
|
5133
5227
|
const worker = this.implWorkers.find((w) => w.cardId === cardId && w.isActive) ?? this.reviewWorkers.find((w) => w.cardId === cardId && w.isActive);
|
|
5134
5228
|
if (!worker) {
|
|
5135
|
-
log.debug(
|
|
5229
|
+
log.debug(TAG24, `No active worker for card ${cardId}, ignoring ${command}`);
|
|
5136
5230
|
return;
|
|
5137
5231
|
}
|
|
5138
|
-
log.info(
|
|
5232
|
+
log.info(TAG24, `Agent command: ${command} → worker ${worker.id} (card ${cardId})`);
|
|
5139
5233
|
switch (command) {
|
|
5140
5234
|
case "pause":
|
|
5141
5235
|
await worker.pause();
|
|
@@ -5181,19 +5275,20 @@ class Pool {
|
|
|
5181
5275
|
};
|
|
5182
5276
|
}
|
|
5183
5277
|
async shutdown() {
|
|
5184
|
-
log.info(
|
|
5278
|
+
log.info(TAG24, "Shutting down pool...");
|
|
5185
5279
|
const active = [
|
|
5186
5280
|
...this.implWorkers.filter((w) => w.isActive),
|
|
5187
5281
|
...this.reviewWorkers.filter((w) => w.isActive)
|
|
5188
5282
|
];
|
|
5189
5283
|
await Promise.all(active.map((w) => w.cancel()));
|
|
5190
|
-
|
|
5284
|
+
this.sleepGuard.stop();
|
|
5285
|
+
log.info(TAG24, "Pool shutdown complete");
|
|
5191
5286
|
}
|
|
5192
5287
|
cardDataCache = new Map;
|
|
5193
5288
|
tryDispatchFor(workers, queue, label) {
|
|
5194
5289
|
const idle = workers.find((w) => w.isIdle);
|
|
5195
5290
|
if (!idle) {
|
|
5196
|
-
log.debug(
|
|
5291
|
+
log.debug(TAG24, `No idle ${label} workers (queue: ${queue.length})`);
|
|
5197
5292
|
return false;
|
|
5198
5293
|
}
|
|
5199
5294
|
const next = queue.dequeue();
|
|
@@ -5201,21 +5296,23 @@ class Pool {
|
|
|
5201
5296
|
return false;
|
|
5202
5297
|
const data = this.cardDataCache.get(next.cardId);
|
|
5203
5298
|
if (!data) {
|
|
5204
|
-
log.warn(
|
|
5299
|
+
log.warn(TAG24, `No cached data for card ${next.cardId}, skipping`);
|
|
5205
5300
|
return false;
|
|
5206
5301
|
}
|
|
5207
5302
|
this.cardDataCache.delete(next.cardId);
|
|
5208
5303
|
this.lastWaitingEmit.delete(next.cardId);
|
|
5209
|
-
log.info(
|
|
5304
|
+
log.info(TAG24, `Dispatching #${next.shortId} to ${label} worker ${idle.id}`);
|
|
5305
|
+
this.sleepGuard.acquire();
|
|
5210
5306
|
idle.run(data.card, data.column, data.labels, data.subtasks);
|
|
5211
5307
|
return true;
|
|
5212
5308
|
}
|
|
5213
5309
|
}
|
|
5214
|
-
var
|
|
5310
|
+
var TAG24 = "pool";
|
|
5215
5311
|
var init_pool = __esm(() => {
|
|
5216
5312
|
init_log();
|
|
5217
5313
|
init_queue();
|
|
5218
5314
|
init_review_worker();
|
|
5315
|
+
init_sleep_guard();
|
|
5219
5316
|
init_types();
|
|
5220
5317
|
init_unblock();
|
|
5221
5318
|
init_worker();
|
|
@@ -5237,7 +5334,7 @@ async function fetchCardSafely(client, cardId) {
|
|
|
5237
5334
|
const { card } = await client.getCard(cardId);
|
|
5238
5335
|
return card;
|
|
5239
5336
|
} catch (err) {
|
|
5240
|
-
log.warn(
|
|
5337
|
+
log.warn(TAG25, `cannot fetch card ${cardId}: ${err instanceof Error ? err.message : err}`);
|
|
5241
5338
|
return null;
|
|
5242
5339
|
}
|
|
5243
5340
|
}
|
|
@@ -5247,7 +5344,7 @@ async function recoverOrphans(store, client, config) {
|
|
|
5247
5344
|
return [];
|
|
5248
5345
|
}
|
|
5249
5346
|
const outcomes = [];
|
|
5250
|
-
log.info(
|
|
5347
|
+
log.info(TAG25, `recovering ${active.length} orphan run(s) from prior daemon`);
|
|
5251
5348
|
for (const run of active) {
|
|
5252
5349
|
const outcome = {
|
|
5253
5350
|
runId: run.runId,
|
|
@@ -5259,11 +5356,11 @@ async function recoverOrphans(store, client, config) {
|
|
|
5259
5356
|
};
|
|
5260
5357
|
outcomes.push(outcome);
|
|
5261
5358
|
if (isProcessAlive(run.daemonPid, process.pid)) {
|
|
5262
|
-
log.warn(
|
|
5359
|
+
log.warn(TAG25, `run ${run.runId} claims live daemon pid ${run.daemonPid} — skipping`);
|
|
5263
5360
|
outcome.actions.push("skipped: daemon pid still alive");
|
|
5264
5361
|
continue;
|
|
5265
5362
|
}
|
|
5266
|
-
log.info(
|
|
5363
|
+
log.info(TAG25, `recovering ${run.pipeline} run ${run.runId} for card #${run.cardShortId}`);
|
|
5267
5364
|
await recoverRun(run, store, client, config, outcome);
|
|
5268
5365
|
}
|
|
5269
5366
|
return outcomes;
|
|
@@ -5281,7 +5378,7 @@ async function recoverRun(run, store, client, config, outcome) {
|
|
|
5281
5378
|
} catch (err) {
|
|
5282
5379
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5283
5380
|
outcome.errors.push(`endAgentSession: ${msg}`);
|
|
5284
|
-
log.warn(
|
|
5381
|
+
log.warn(TAG25, `endAgentSession failed for ${run.cardId}: ${msg}`);
|
|
5285
5382
|
}
|
|
5286
5383
|
const card = await fetchCardSafely(client, run.cardId);
|
|
5287
5384
|
if (card) {
|
|
@@ -5322,9 +5419,9 @@ async function recoverRun(run, store, client, config, outcome) {
|
|
|
5322
5419
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5323
5420
|
outcome.errors.push(`endRun: ${msg}`);
|
|
5324
5421
|
}
|
|
5325
|
-
log.info(
|
|
5422
|
+
log.info(TAG25, `recovered run ${run.runId} (card #${run.cardShortId}): ${outcome.actions.join(", ")}${outcome.errors.length ? ` | errors: ${outcome.errors.join("; ")}` : ""}`);
|
|
5326
5423
|
}
|
|
5327
|
-
var
|
|
5424
|
+
var TAG25 = "recovery", RECOVERED_LABEL = "agent-recovered", RECOVERED_LABEL_COLOR = "#f59e0b";
|
|
5328
5425
|
var init_recovery = __esm(() => {
|
|
5329
5426
|
init_board_helpers();
|
|
5330
5427
|
init_log();
|
|
@@ -5372,7 +5469,7 @@ class Reconciler {
|
|
|
5372
5469
|
clearInterval(this.timer);
|
|
5373
5470
|
this.timer = null;
|
|
5374
5471
|
}
|
|
5375
|
-
log.info(
|
|
5472
|
+
log.info(TAG26, "Heartbeat stopped");
|
|
5376
5473
|
}
|
|
5377
5474
|
async recoverStaleRuns() {
|
|
5378
5475
|
if (!this.stateStore || !this.agentConfig)
|
|
@@ -5389,7 +5486,7 @@ class Reconciler {
|
|
|
5389
5486
|
if (!daemonDead && !(heartbeatStale && ourZombie))
|
|
5390
5487
|
continue;
|
|
5391
5488
|
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`;
|
|
5392
|
-
log.warn(
|
|
5489
|
+
log.warn(TAG26, `zombie run ${run.runId} (#${run.cardShortId}): ${reason} — recovering`);
|
|
5393
5490
|
await recoverRun(run, this.stateStore, this.client, this.agentConfig, {
|
|
5394
5491
|
runId: run.runId,
|
|
5395
5492
|
cardId: run.cardId,
|
|
@@ -5425,18 +5522,18 @@ class Reconciler {
|
|
|
5425
5522
|
const subtasks = card.subtasks ?? [];
|
|
5426
5523
|
const mode = reviewColumnIds.has(card.column_id) ? "review" : "implement";
|
|
5427
5524
|
if (mode === "review" && this.approvedLabel && hasLabel(cardLabels, this.approvedLabel)) {
|
|
5428
|
-
log.debug(
|
|
5525
|
+
log.debug(TAG26, `Skipping #${card.short_id} — already has "${this.approvedLabel}" label`);
|
|
5429
5526
|
continue;
|
|
5430
5527
|
}
|
|
5431
5528
|
if (mode === "review" && hasLabel(cardLabels, NEED_REVIEW_LABEL)) {
|
|
5432
|
-
log.debug(
|
|
5529
|
+
log.debug(TAG26, `Skipping #${card.short_id} — has "${NEED_REVIEW_LABEL}" label (needs human)`);
|
|
5433
5530
|
continue;
|
|
5434
5531
|
}
|
|
5435
5532
|
if (mode === "review" && !extractBranchFromDescription(card.description)) {
|
|
5436
|
-
log.debug(
|
|
5533
|
+
log.debug(TAG26, `Skipping #${card.short_id} — no branch reference (not qualified for auto-review)`);
|
|
5437
5534
|
continue;
|
|
5438
5535
|
}
|
|
5439
|
-
log.info(
|
|
5536
|
+
log.info(TAG26, `Missed assignment: #${card.short_id} "${card.title}" (${mode}) — enqueueing`);
|
|
5440
5537
|
await this.pool.enqueue(card, column, cardLabels, subtasks, mode);
|
|
5441
5538
|
}
|
|
5442
5539
|
}
|
|
@@ -5445,17 +5542,17 @@ class Reconciler {
|
|
|
5445
5542
|
}
|
|
5446
5543
|
for (const knownId of knownCardIds) {
|
|
5447
5544
|
if (!allAgentCardIds.has(knownId)) {
|
|
5448
|
-
log.info(
|
|
5545
|
+
log.info(TAG26, `Missed unassign: ${knownId} — removing`);
|
|
5449
5546
|
await this.pool.removeCard(knownId);
|
|
5450
5547
|
}
|
|
5451
5548
|
}
|
|
5452
|
-
log.debug(
|
|
5549
|
+
log.debug(TAG26, `Reconciled: ${assignedCards.length} assigned, ${knownCardIds.size} known`);
|
|
5453
5550
|
} catch (err) {
|
|
5454
|
-
log.error(
|
|
5551
|
+
log.error(TAG26, `Heartbeat failed: ${err instanceof Error ? err.message : err}`);
|
|
5455
5552
|
}
|
|
5456
5553
|
}
|
|
5457
5554
|
}
|
|
5458
|
-
var
|
|
5555
|
+
var TAG26 = "reconcile";
|
|
5459
5556
|
var init_reconcile = __esm(() => {
|
|
5460
5557
|
init_board_helpers();
|
|
5461
5558
|
init_log();
|
|
@@ -5489,7 +5586,7 @@ function prettyBanner(config, version) {
|
|
|
5489
5586
|
checks.push({ kind: "ok", message });
|
|
5490
5587
|
},
|
|
5491
5588
|
warn(message) {
|
|
5492
|
-
log.warn(
|
|
5589
|
+
log.warn(TAG27, message);
|
|
5493
5590
|
checks.push({ kind: "warn", message: message.split(`
|
|
5494
5591
|
`, 1)[0] });
|
|
5495
5592
|
},
|
|
@@ -5513,22 +5610,22 @@ function prettyBanner(config, version) {
|
|
|
5513
5610
|
};
|
|
5514
5611
|
}
|
|
5515
5612
|
function jsonBanner(config, version) {
|
|
5516
|
-
log.info(
|
|
5517
|
-
log.info(
|
|
5613
|
+
log.info(TAG27, `Harmony Agent Daemon v${version} starting...`);
|
|
5614
|
+
log.info(TAG27, `Project: ${config.projectId} | Pool: ${config.agent.poolSize} | Model: ${config.agent.claude.model} | Pickup: ${config.agent.pickupColumns.join(", ")}`);
|
|
5518
5615
|
if (config.agent.review.enabled) {
|
|
5519
|
-
log.info(
|
|
5616
|
+
log.info(TAG27, `Review: enabled | Columns: ${config.agent.review.pickupColumns.join(", ")} | → ${config.agent.review.moveToColumn} / ${config.agent.review.failColumn}`);
|
|
5520
5617
|
}
|
|
5521
5618
|
let failed = false;
|
|
5522
5619
|
return {
|
|
5523
5620
|
setProjectName(_name) {},
|
|
5524
5621
|
setGitProvider(provider) {
|
|
5525
|
-
log.info(
|
|
5622
|
+
log.info(TAG27, `Git provider: ${provider}`);
|
|
5526
5623
|
},
|
|
5527
5624
|
check(message) {
|
|
5528
|
-
log.info(
|
|
5625
|
+
log.info(TAG27, message);
|
|
5529
5626
|
},
|
|
5530
5627
|
warn(message) {
|
|
5531
|
-
log.warn(
|
|
5628
|
+
log.warn(TAG27, message);
|
|
5532
5629
|
},
|
|
5533
5630
|
fail() {
|
|
5534
5631
|
failed = true;
|
|
@@ -5536,7 +5633,7 @@ function jsonBanner(config, version) {
|
|
|
5536
5633
|
async ready(message) {
|
|
5537
5634
|
if (failed)
|
|
5538
5635
|
return;
|
|
5539
|
-
log.info(
|
|
5636
|
+
log.info(TAG27, message);
|
|
5540
5637
|
}
|
|
5541
5638
|
};
|
|
5542
5639
|
}
|
|
@@ -5603,7 +5700,7 @@ function cyan(s) {
|
|
|
5603
5700
|
function yellow(s) {
|
|
5604
5701
|
return `${ANSI.yellow}${s}${ANSI.reset}`;
|
|
5605
5702
|
}
|
|
5606
|
-
var
|
|
5703
|
+
var TAG27 = "daemon", RULE_WIDTH = 70, ANSI;
|
|
5607
5704
|
var init_startup_banner = __esm(() => {
|
|
5608
5705
|
init_log();
|
|
5609
5706
|
ANSI = {
|
|
@@ -5750,18 +5847,18 @@ class Watcher {
|
|
|
5750
5847
|
}
|
|
5751
5848
|
async start() {
|
|
5752
5849
|
if (!isPretty()) {
|
|
5753
|
-
log.info(
|
|
5850
|
+
log.info(TAG28, "Connecting to Supabase realtime (broadcast)...");
|
|
5754
5851
|
}
|
|
5755
5852
|
this.supabase = createClient(this.credentials.supabaseUrl, this.credentials.supabaseAnonKey);
|
|
5756
5853
|
const presenceChannel = this.supabase.channel(`board-presence-${this.projectId}`);
|
|
5757
5854
|
const channel = this.supabase.channel(`board-${this.projectId}`).on("broadcast", { event: "card_update" }, (msg) => {
|
|
5758
|
-
log.debug(
|
|
5855
|
+
log.debug(TAG28, `Broadcast: card_update ${JSON.stringify(msg.payload)}`);
|
|
5759
5856
|
this.onCardBroadcast({
|
|
5760
5857
|
event: "card_update",
|
|
5761
5858
|
payload: msg.payload ?? {}
|
|
5762
5859
|
});
|
|
5763
5860
|
}).on("broadcast", { event: "card_created" }, (msg) => {
|
|
5764
|
-
log.debug(
|
|
5861
|
+
log.debug(TAG28, `Broadcast: card_created ${JSON.stringify(msg.payload)}`);
|
|
5765
5862
|
this.onCardBroadcast({
|
|
5766
5863
|
event: "card_created",
|
|
5767
5864
|
payload: msg.payload ?? {}
|
|
@@ -5771,29 +5868,29 @@ class Watcher {
|
|
|
5771
5868
|
const cardId = payload.card_id;
|
|
5772
5869
|
const command = payload.command;
|
|
5773
5870
|
if (cardId && command) {
|
|
5774
|
-
log.info(
|
|
5871
|
+
log.info(TAG28, `Broadcast: agent_command ${command} for ${cardId}`);
|
|
5775
5872
|
this.onAgentCommand?.({ cardId, command });
|
|
5776
5873
|
}
|
|
5777
5874
|
}).subscribe((status) => {
|
|
5778
5875
|
if (status === "SUBSCRIBED") {
|
|
5779
5876
|
this.connected = true;
|
|
5780
5877
|
if (!isPretty() || !this.suppressStartupLogs) {
|
|
5781
|
-
log.info(
|
|
5878
|
+
log.info(TAG28, "Broadcast subscription active");
|
|
5782
5879
|
}
|
|
5783
5880
|
this.maybeResolveReady();
|
|
5784
5881
|
} else if (status === "CHANNEL_ERROR") {
|
|
5785
5882
|
this.connected = false;
|
|
5786
|
-
log.error(
|
|
5883
|
+
log.error(TAG28, "Broadcast channel error — will rely on reconciliation");
|
|
5787
5884
|
} else if (status === "TIMED_OUT") {
|
|
5788
5885
|
this.connected = false;
|
|
5789
|
-
log.warn(
|
|
5886
|
+
log.warn(TAG28, "Broadcast subscription timed out — retrying...");
|
|
5790
5887
|
} else if (status === "CLOSED") {
|
|
5791
5888
|
this.connected = false;
|
|
5792
5889
|
}
|
|
5793
5890
|
});
|
|
5794
5891
|
this.channel = channel;
|
|
5795
5892
|
presenceChannel.on("presence", { event: "sync" }, () => {
|
|
5796
|
-
log.debug(
|
|
5893
|
+
log.debug(TAG28, "Presence sync");
|
|
5797
5894
|
}).subscribe(async (status) => {
|
|
5798
5895
|
if (status === "SUBSCRIBED") {
|
|
5799
5896
|
await presenceChannel.track({
|
|
@@ -5805,7 +5902,7 @@ class Watcher {
|
|
|
5805
5902
|
agentName: this.identity.agentName
|
|
5806
5903
|
});
|
|
5807
5904
|
if (!isPretty() || !this.suppressStartupLogs) {
|
|
5808
|
-
log.info(
|
|
5905
|
+
log.info(TAG28, "Presence tracked on board-presence channel");
|
|
5809
5906
|
}
|
|
5810
5907
|
this.presenceTracked = true;
|
|
5811
5908
|
this.maybeResolveReady();
|
|
@@ -5827,10 +5924,10 @@ class Watcher {
|
|
|
5827
5924
|
this.supabase = null;
|
|
5828
5925
|
}
|
|
5829
5926
|
this.connected = false;
|
|
5830
|
-
log.info(
|
|
5927
|
+
log.info(TAG28, "Broadcast subscription stopped");
|
|
5831
5928
|
}
|
|
5832
5929
|
}
|
|
5833
|
-
var
|
|
5930
|
+
var TAG28 = "watcher";
|
|
5834
5931
|
var init_watcher = __esm(() => {
|
|
5835
5932
|
init_log();
|
|
5836
5933
|
});
|
|
@@ -5905,10 +6002,10 @@ function runWorktreeGc(basePath, store, opts = {}) {
|
|
|
5905
6002
|
});
|
|
5906
6003
|
} catch {}
|
|
5907
6004
|
if (result.removed.length > 0) {
|
|
5908
|
-
log.info(
|
|
6005
|
+
log.info(TAG29, `GC removed ${result.removed.length} orphan worktree(s): ${result.removed.map((p) => p.split("/").pop()).join(", ")}`);
|
|
5909
6006
|
}
|
|
5910
6007
|
if (result.errors.length > 0) {
|
|
5911
|
-
log.warn(
|
|
6008
|
+
log.warn(TAG29, `GC had ${result.errors.length} error(s): ${result.errors.map((e) => `${e.path}: ${e.error}`).join("; ")}`);
|
|
5912
6009
|
}
|
|
5913
6010
|
return result;
|
|
5914
6011
|
}
|
|
@@ -5985,10 +6082,10 @@ function pruneFailedRemoteBranches(opts) {
|
|
|
5985
6082
|
}
|
|
5986
6083
|
}
|
|
5987
6084
|
if (result.removed.length > 0) {
|
|
5988
|
-
log.info(
|
|
6085
|
+
log.info(TAG29, `Pruned ${result.removed.length} stale remote branch(es) under ${opts.prefix}: ${result.removed.join(", ")}`);
|
|
5989
6086
|
}
|
|
5990
6087
|
if (result.errors.length > 0) {
|
|
5991
|
-
log.warn(
|
|
6088
|
+
log.warn(TAG29, `Remote branch GC had ${result.errors.length} error(s): ${result.errors.map((e) => `${e.ref}: ${e.error}`).join("; ")}`);
|
|
5992
6089
|
}
|
|
5993
6090
|
return result;
|
|
5994
6091
|
}
|
|
@@ -6019,13 +6116,13 @@ class WorktreeGc {
|
|
|
6019
6116
|
try {
|
|
6020
6117
|
runWorktreeGc(this.basePath, this.store);
|
|
6021
6118
|
} catch (err) {
|
|
6022
|
-
log.warn(
|
|
6119
|
+
log.warn(TAG29, `GC tick failed: ${err instanceof Error ? err.message : err}`);
|
|
6023
6120
|
}
|
|
6024
6121
|
if (this.remoteOpts) {
|
|
6025
6122
|
try {
|
|
6026
6123
|
pruneFailedRemoteBranches(this.remoteOpts);
|
|
6027
6124
|
} catch (err) {
|
|
6028
|
-
log.warn(
|
|
6125
|
+
log.warn(TAG29, `Remote GC tick failed: ${err instanceof Error ? err.message : err}`);
|
|
6029
6126
|
}
|
|
6030
6127
|
}
|
|
6031
6128
|
}
|
|
@@ -6039,7 +6136,7 @@ function getRepoRoot2() {
|
|
|
6039
6136
|
return null;
|
|
6040
6137
|
}
|
|
6041
6138
|
}
|
|
6042
|
-
var
|
|
6139
|
+
var TAG29 = "worktree-gc";
|
|
6043
6140
|
var init_worktree_gc = __esm(() => {
|
|
6044
6141
|
init_log();
|
|
6045
6142
|
init_worktree();
|
|
@@ -6121,7 +6218,7 @@ async function main() {
|
|
|
6121
6218
|
} catch (err) {
|
|
6122
6219
|
if (err instanceof ConfigValidationError) {
|
|
6123
6220
|
banner.fail();
|
|
6124
|
-
log.error(
|
|
6221
|
+
log.error(TAG30, err.message);
|
|
6125
6222
|
process.exit(1);
|
|
6126
6223
|
}
|
|
6127
6224
|
throw err;
|
|
@@ -6222,25 +6319,25 @@ async function main() {
|
|
|
6222
6319
|
if (shuttingDown)
|
|
6223
6320
|
return;
|
|
6224
6321
|
shuttingDown = true;
|
|
6225
|
-
log.info(
|
|
6322
|
+
log.info(TAG30, `Received ${signal}, shutting down gracefully...`);
|
|
6226
6323
|
reconciler.stop();
|
|
6227
6324
|
mergeMonitor?.stop();
|
|
6228
6325
|
worktreeGc.stop();
|
|
6229
6326
|
await httpServer?.stop();
|
|
6230
6327
|
await watcher.stop();
|
|
6231
6328
|
await pool.shutdown();
|
|
6232
|
-
log.info(
|
|
6329
|
+
log.info(TAG30, "Daemon stopped.");
|
|
6233
6330
|
process.exit(exitCode);
|
|
6234
6331
|
};
|
|
6235
6332
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
6236
6333
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
6237
6334
|
process.on("uncaughtException", (err) => {
|
|
6238
|
-
log.error(
|
|
6335
|
+
log.error(TAG30, `Uncaught exception: ${err.message}`);
|
|
6239
6336
|
exitCode = 1;
|
|
6240
6337
|
shutdown("uncaughtException");
|
|
6241
6338
|
});
|
|
6242
6339
|
process.on("unhandledRejection", (reason) => {
|
|
6243
|
-
log.error(
|
|
6340
|
+
log.error(TAG30, `Unhandled rejection: ${reason instanceof Error ? reason.message : String(reason)}`);
|
|
6244
6341
|
exitCode = 1;
|
|
6245
6342
|
shutdown("unhandledRejection");
|
|
6246
6343
|
});
|
|
@@ -6287,14 +6384,14 @@ async function handleBroadcast(event, client, pool, config, agentUserId) {
|
|
|
6287
6384
|
if (assigneeId === undefined)
|
|
6288
6385
|
return;
|
|
6289
6386
|
if (assigneeId === agentUserId) {
|
|
6290
|
-
log.info(
|
|
6387
|
+
log.info(TAG30, `Broadcast: card ${cardId} assigned to agent`);
|
|
6291
6388
|
try {
|
|
6292
6389
|
await tryEnqueueCard(cardId, client, pool, config);
|
|
6293
6390
|
} catch (err) {
|
|
6294
|
-
log.error(
|
|
6391
|
+
log.error(TAG30, `Failed to process assignment: ${err instanceof Error ? err.message : err}`);
|
|
6295
6392
|
}
|
|
6296
6393
|
} else if (pool.isCardKnown(cardId)) {
|
|
6297
|
-
log.info(
|
|
6394
|
+
log.info(TAG30, `Broadcast: card ${cardId} unassigned from agent`);
|
|
6298
6395
|
await pool.removeCard(cardId);
|
|
6299
6396
|
}
|
|
6300
6397
|
}
|
|
@@ -6304,13 +6401,13 @@ async function tryEnqueueCard(cardId, client, pool, config) {
|
|
|
6304
6401
|
const columns = board.columns;
|
|
6305
6402
|
const column = columns.find((c) => c.id === card.column_id);
|
|
6306
6403
|
if (!column) {
|
|
6307
|
-
log.warn(
|
|
6404
|
+
log.warn(TAG30, `Column not found for card ${cardId}`);
|
|
6308
6405
|
return;
|
|
6309
6406
|
}
|
|
6310
6407
|
const isPickupColumn = config.agent.pickupColumns.some((name) => name.toLowerCase() === column.name.toLowerCase());
|
|
6311
6408
|
const isReviewColumn = config.agent.review.enabled && config.agent.review.pickupColumns.some((name) => name.toLowerCase() === column.name.toLowerCase());
|
|
6312
6409
|
if (!isPickupColumn && !isReviewColumn) {
|
|
6313
|
-
log.info(
|
|
6410
|
+
log.info(TAG30, `Card #${card.short_id} is in "${column.name}", not a pickup/review column — skipping`);
|
|
6314
6411
|
return;
|
|
6315
6412
|
}
|
|
6316
6413
|
const mode = isReviewColumn ? "review" : "implement";
|
|
@@ -6318,16 +6415,16 @@ async function tryEnqueueCard(cardId, client, pool, config) {
|
|
|
6318
6415
|
const cardLabels = resolveCardLabels(card, labelMap);
|
|
6319
6416
|
const subtasks = card.subtasks ?? [];
|
|
6320
6417
|
if (mode === "review" && config.agent.review.approvedLabel && hasLabel(cardLabels, config.agent.review.approvedLabel)) {
|
|
6321
|
-
log.debug(
|
|
6418
|
+
log.debug(TAG30, `Card #${card.short_id} already has "${config.agent.review.approvedLabel}" — skipping review`);
|
|
6322
6419
|
return;
|
|
6323
6420
|
}
|
|
6324
6421
|
if (mode === "review" && !extractBranchFromDescription(card.description)) {
|
|
6325
|
-
log.info(
|
|
6422
|
+
log.info(TAG30, `Card #${card.short_id} has no branch reference — skipping auto-review`);
|
|
6326
6423
|
return;
|
|
6327
6424
|
}
|
|
6328
6425
|
await pool.enqueue(card, column, cardLabels, subtasks, mode);
|
|
6329
6426
|
}
|
|
6330
|
-
var
|
|
6427
|
+
var TAG30 = "daemon", PKG_VERSION;
|
|
6331
6428
|
var init_src = __esm(() => {
|
|
6332
6429
|
init_board_helpers();
|
|
6333
6430
|
init_config();
|