@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.
Files changed (3) hide show
  1. package/dist/cli.js +207 -90
  2. package/dist/index.js +207 -90
  3. 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(TAG20, `Generated prompt for #${card.short_id} — ${result.contextSummary.memoryCount} memories, ${result.tokenEstimate} tokens`);
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(TAG20, `Failed to generate prompt via API, using fallback: ${msg}`);
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(TAG20, "comment-thread fetch failed", {
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(TAG20, "past-episodes recall failed", {
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 TAG20 = "prompt";
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 `${TAG21}:${this.id}`;
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(TAG21, "startAgentSession returned no session id");
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 TAG21 = "worker", CANCEL_SIGINT_TIMEOUT2 = 30000, CANCEL_SIGTERM_TIMEOUT2 = 1e4;
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(TAG22, `Card ${card.id} already queued or active, skipping`);
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(TAG22, `#${card.short_id} skipped (daily_budget): ${decision.detail}`);
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(TAG22, `#${card.short_id} gave up: ${decision.detail}`);
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(TAG22, `waiting emit failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
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(TAG22, `Removed #${removed.shortId} from ${removed.mode} queue`);
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(TAG22, `Cancelling worker ${worker.id} for card ${cardId}`);
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(TAG22, `No active worker for card ${cardId}, ignoring ${command}`);
5135
+ log.debug(TAG23, `No active worker for card ${cardId}, ignoring ${command}`);
5041
5136
  return;
5042
5137
  }
5043
- log.info(TAG22, `Agent command: ${command} → worker ${worker.id} (card ${cardId})`);
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(TAG22, "Shutting down pool...");
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(TAG22, "Pool shutdown complete");
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(TAG22, `No idle ${label} workers (queue: ${queue.length})`);
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(TAG22, `No cached data for card ${next.cardId}, skipping`);
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
- log.info(TAG22, `Dispatching #${next.shortId} to ${label} worker ${idle.id}`);
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 TAG22 = "pool";
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(TAG23, `cannot fetch card ${cardId}: ${err instanceof Error ? err.message : err}`);
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(TAG23, `recovering ${active.length} orphan run(s) from prior daemon`);
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(TAG23, `run ${run.runId} claims live daemon pid ${run.daemonPid} — skipping`);
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(TAG23, `recovering ${run.pipeline} run ${run.runId} for card #${run.cardShortId}`);
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(TAG23, `endAgentSession failed for ${run.cardId}: ${msg}`);
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(TAG23, `recovered run ${run.runId} (card #${run.cardShortId}): ${outcome.actions.join(", ")}${outcome.errors.length ? ` | errors: ${outcome.errors.join("; ")}` : ""}`);
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 TAG23 = "recovery", RECOVERED_LABEL = "agent-recovered", RECOVERED_LABEL_COLOR = "#f59e0b";
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(TAG24, "Heartbeat stopped");
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(TAG24, `zombie run ${run.runId} (#${run.cardShortId}): ${reason} — recovering`);
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(TAG24, `Skipping #${card.short_id} — already has "${this.approvedLabel}" label`);
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(TAG24, `Skipping #${card.short_id} — has "${NEED_REVIEW_LABEL}" label (needs human)`);
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(TAG24, `Skipping #${card.short_id} — no branch reference (not qualified for auto-review)`);
5436
+ log.debug(TAG25, `Skipping #${card.short_id} — no branch reference (not qualified for auto-review)`);
5340
5437
  continue;
5341
5438
  }
5342
- log.info(TAG24, `Missed assignment: #${card.short_id} "${card.title}" (${mode}) — enqueueing`);
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(TAG24, `Missed unassign: ${knownId} — removing`);
5448
+ log.info(TAG25, `Missed unassign: ${knownId} — removing`);
5352
5449
  await this.pool.removeCard(knownId);
5353
5450
  }
5354
5451
  }
5355
- log.debug(TAG24, `Reconciled: ${assignedCards.length} assigned, ${knownCardIds.size} known`);
5452
+ log.debug(TAG25, `Reconciled: ${assignedCards.length} assigned, ${knownCardIds.size} known`);
5356
5453
  } catch (err) {
5357
- log.error(TAG24, `Heartbeat failed: ${err instanceof Error ? err.message : err}`);
5454
+ log.error(TAG25, `Heartbeat failed: ${err instanceof Error ? err.message : err}`);
5358
5455
  }
5359
5456
  }
5360
5457
  }
5361
- var TAG24 = "reconcile";
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(TAG25, `Harmony Agent Daemon v${version} starting...`);
5415
- log.info(TAG25, `Project: ${config.projectId} | Pool: ${config.agent.poolSize} | Model: ${config.agent.claude.model} | Pickup: ${config.agent.pickupColumns.join(", ")}`);
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(TAG25, `Review: enabled | Columns: ${config.agent.review.pickupColumns.join(", ")} | → ${config.agent.review.moveToColumn} / ${config.agent.review.failColumn}`);
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(TAG25, `Git provider: ${provider}`);
5525
+ log.info(TAG26, `Git provider: ${provider}`);
5424
5526
  },
5425
5527
  check(message) {
5426
- log.info(TAG25, message);
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(TAG25, message);
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 msg of checks) {
5449
- lines.push(` ${cyan("✓")} ${msg}`);
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
- var TAG25 = "daemon", RULE_WIDTH = 70, ANSI;
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(TAG26, "Connecting to Supabase realtime (broadcast)...");
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(TAG26, `Broadcast: card_update ${JSON.stringify(msg.payload)}`);
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(TAG26, `Broadcast: card_created ${JSON.stringify(msg.payload)}`);
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(TAG26, `Broadcast: agent_command ${command} for ${cardId}`);
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(TAG26, "Broadcast subscription active");
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(TAG26, "Broadcast channel error — will rely on reconciliation");
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(TAG26, "Broadcast subscription timed out — retrying...");
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(TAG26, "Presence sync");
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(TAG26, "Presence tracked on board-presence channel");
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(TAG26, "Broadcast subscription stopped");
5830
+ log.info(TAG27, "Broadcast subscription stopped");
5722
5831
  }
5723
5832
  }
5724
- var TAG26 = "watcher";
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(TAG27, `GC removed ${result.removed.length} orphan worktree(s): ${result.removed.map((p) => p.split("/").pop()).join(", ")}`);
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(TAG27, `GC had ${result.errors.length} error(s): ${result.errors.map((e) => `${e.path}: ${e.error}`).join("; ")}`);
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(TAG27, `Pruned ${result.removed.length} stale remote branch(es) under ${opts.prefix}: ${result.removed.join(", ")}`);
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(TAG27, `Remote branch GC had ${result.errors.length} error(s): ${result.errors.map((e) => `${e.ref}: ${e.error}`).join("; ")}`);
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(TAG27, `GC tick failed: ${err instanceof Error ? err.message : err}`);
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(TAG27, `Remote GC tick failed: ${err instanceof Error ? err.message : err}`);
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 TAG27 = "worktree-gc";
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.fail();
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(TAG28, err.message);
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(TAG28, `Received ${signal}, shutting down gracefully...`);
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(TAG28, "Daemon stopped.");
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(TAG28, `Uncaught exception: ${err.message}`);
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(TAG28, `Unhandled rejection: ${reason instanceof Error ? reason.message : String(reason)}`);
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.fail();
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(TAG28, `Broadcast: card ${cardId} assigned to agent`);
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(TAG28, `Failed to process assignment: ${err instanceof Error ? err.message : err}`);
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(TAG28, `Broadcast: card ${cardId} unassigned from agent`);
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(TAG28, `Column not found for card ${cardId}`);
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(TAG28, `Card #${card.short_id} is in "${column.name}", not a pickup/review column — skipping`);
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(TAG28, `Card #${card.short_id} already has "${config.agent.review.approvedLabel}" — skipping review`);
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(TAG28, `Card #${card.short_id} has no branch reference — skipping auto-review`);
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 TAG28 = "daemon", PKG_VERSION;
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"));