@gethmy/agent 1.10.0 → 1.10.2

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 +79 -30
  2. package/dist/index.js +75 -28
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1559,14 +1559,6 @@ class BudgetGuard {
1559
1559
  detail: `${card.attempts} of ${this.config.maxAttemptsPerCard} attempts exhausted`
1560
1560
  };
1561
1561
  }
1562
- const dailySpent = this.store.getDailyCostCents();
1563
- if (dailySpent >= this.config.dailyBudgetCents) {
1564
- return {
1565
- allow: false,
1566
- reason: "daily_budget",
1567
- detail: `daily budget ${formatCents(dailySpent)}/${formatCents(this.config.dailyBudgetCents)} exhausted`
1568
- };
1569
- }
1570
1562
  return { allow: true };
1571
1563
  }
1572
1564
  }
@@ -1590,9 +1582,6 @@ function buildGaveUpComment(maxAttempts, failures) {
1590
1582
  return lines.join(`
1591
1583
  `);
1592
1584
  }
1593
- function formatCents(cents) {
1594
- return `$${(cents / 100).toFixed(2)}`;
1595
- }
1596
1585
 
1597
1586
  // src/error-classifier.ts
1598
1587
  function parseRetryAfterMs(message) {
@@ -2329,7 +2318,8 @@ function buildTokenPayload(stats) {
2329
2318
  outputTokens: stats.cost.totalOutputTokens,
2330
2319
  cacheCreationInputTokens: stats.cost.totalCacheCreationInputTokens,
2331
2320
  cacheReadInputTokens: stats.cost.totalCacheReadInputTokens,
2332
- modelName: stats.cost.modelName
2321
+ modelName: stats.cost.modelName,
2322
+ numTurns: stats.cost.numTurns
2333
2323
  };
2334
2324
  }
2335
2325
  async function runCompletion(client, card, branchName, worktreePath, config, workerId, sessionStats, workspaceId, agentSessionId, stateStore, onMovedToCompletion) {
@@ -2915,7 +2905,8 @@ class ProgressTracker {
2915
2905
  outputTokens: this.lastCost?.totalOutputTokens ?? 0,
2916
2906
  cacheCreationInputTokens: this.lastCost?.totalCacheCreationInputTokens ?? 0,
2917
2907
  cacheReadInputTokens: this.lastCost?.totalCacheReadInputTokens ?? 0,
2918
- modelName: this.lastCost?.modelName
2908
+ modelName: this.lastCost?.modelName,
2909
+ numTurns: this.lastCost?.numTurns ?? 0
2919
2910
  }).catch((err) => {
2920
2911
  log.warn(TAG14, `Failed to send progress update: ${err}`);
2921
2912
  });
@@ -3686,13 +3677,19 @@ class StateStore {
3686
3677
  heartbeat(runId) {
3687
3678
  return this.updateRun(runId, { lastHeartbeatAt: Date.now() });
3688
3679
  }
3689
- endRun(runId, status, errorMessage) {
3680
+ endRun(runId, status, opts = {}) {
3690
3681
  const patch = {
3691
3682
  status,
3692
3683
  endedAt: Date.now()
3693
3684
  };
3694
- if (errorMessage !== undefined)
3695
- patch.errorMessage = errorMessage;
3685
+ if (opts.errorMessage !== undefined)
3686
+ patch.errorMessage = opts.errorMessage;
3687
+ if (opts.costCents !== undefined && opts.costCents > 0) {
3688
+ patch.costCents = opts.costCents;
3689
+ }
3690
+ if (opts.numTurns !== undefined && opts.numTurns > 0) {
3691
+ patch.numTurns = opts.numTurns;
3692
+ }
3696
3693
  return this.updateRun(runId, patch);
3697
3694
  }
3698
3695
  getRun(runId) {
@@ -4124,6 +4121,19 @@ class ReviewWorker {
4124
4121
  get isActive() {
4125
4122
  return this.state === "preparing" || this.state === "running" || this.state === "verifying" || this.state === "completing";
4126
4123
  }
4124
+ get costCents() {
4125
+ const cost = (this.progressTracker?.stats ?? this.lastSessionStats)?.cost;
4126
+ return cost ? Math.round(cost.totalCostUsd * 100) : 0;
4127
+ }
4128
+ endLedger() {
4129
+ const cost = (this.lastSessionStats ?? this.progressTracker?.stats)?.cost;
4130
+ if (!cost)
4131
+ return { costCents: 0, numTurns: 0 };
4132
+ return {
4133
+ costCents: Math.round(cost.totalCostUsd * 100),
4134
+ numTurns: cost.numTurns
4135
+ };
4136
+ }
4127
4137
  async run(card, column, labels, subtasks) {
4128
4138
  this.aborted = false;
4129
4139
  this.timedOut = false;
@@ -4149,7 +4159,8 @@ class ReviewWorker {
4149
4159
  lastHeartbeatAt: this.startedAt,
4150
4160
  endedAt: null,
4151
4161
  status: "active",
4152
- costCents: 0
4162
+ costCents: 0,
4163
+ numTurns: 0
4153
4164
  });
4154
4165
  this.branchName = extractBranchFromDescription(card.description);
4155
4166
  const localMode = !this.branchName;
@@ -4320,7 +4331,7 @@ class ReviewWorker {
4320
4331
  }
4321
4332
  if (this.runId) {
4322
4333
  try {
4323
- await this.stateStore.endRun(this.runId, this.state === "error" ? "paused" : "completed");
4334
+ await this.stateStore.endRun(this.runId, this.state === "error" ? "paused" : "completed", this.endLedger());
4324
4335
  } catch {}
4325
4336
  }
4326
4337
  } finally {
@@ -4329,7 +4340,7 @@ class ReviewWorker {
4329
4340
  const run = this.stateStore.getRun(this.runId);
4330
4341
  if (run && run.endedAt === null) {
4331
4342
  const status = this.timedOut ? "failed" : this.state === "error" || this.aborted ? "paused" : "completed";
4332
- await this.stateStore.endRun(this.runId, status);
4343
+ await this.stateStore.endRun(this.runId, status, this.endLedger());
4333
4344
  }
4334
4345
  } catch {}
4335
4346
  }
@@ -5000,6 +5011,8 @@ class Worker {
5000
5011
  verificationFailed = false;
5001
5012
  sessionId = null;
5002
5013
  runId = null;
5014
+ runCostCents = 0;
5015
+ runTurns = 0;
5003
5016
  constructor(id, config, client, agentId, onDone, workspaceId, projectId, stateStore, onCardCompleted, onApiError) {
5004
5017
  this.config = config;
5005
5018
  this.client = client;
@@ -5052,10 +5065,20 @@ class Worker {
5052
5065
  get isActive() {
5053
5066
  return this.state === "preparing" || this.state === "planning" || this.state === "running" || this.state === "verifying" || this.state === "completing";
5054
5067
  }
5068
+ get costCents() {
5069
+ const live = this.progressTracker?.stats.cost;
5070
+ const liveCents = live ? Math.round(live.totalCostUsd * 100) : 0;
5071
+ return this.runCostCents + liveCents;
5072
+ }
5073
+ runLedger() {
5074
+ return { costCents: this.runCostCents, numTurns: this.runTurns };
5075
+ }
5055
5076
  async run(card, column, labels, subtasks) {
5056
5077
  this.aborted = false;
5057
5078
  this.timedOut = false;
5058
5079
  this.verificationFailed = false;
5080
+ this.runCostCents = 0;
5081
+ this.runTurns = 0;
5059
5082
  this.cardId = card.id;
5060
5083
  this.startedAt = Date.now();
5061
5084
  this.runId = newRunId();
@@ -5080,7 +5103,8 @@ class Worker {
5080
5103
  lastHeartbeatAt: this.startedAt,
5081
5104
  endedAt: null,
5082
5105
  status: "active",
5083
- costCents: 0
5106
+ costCents: 0,
5107
+ numTurns: 0
5084
5108
  });
5085
5109
  const { session } = await this.client.startAgentSession(card.id, {
5086
5110
  agentIdentifier: agentIdentifier(this.id),
@@ -5196,7 +5220,10 @@ class Worker {
5196
5220
  }
5197
5221
  if (this.runId) {
5198
5222
  try {
5199
- await this.stateStore.endRun(this.runId, "failed", errClass.kind ?? msg);
5223
+ await this.stateStore.endRun(this.runId, "failed", {
5224
+ errorMessage: errClass.kind ?? msg,
5225
+ ...this.runLedger()
5226
+ });
5200
5227
  } catch {}
5201
5228
  if (apiError) {
5202
5229
  await this.stateStore.decrementAttempt(card.id);
@@ -5208,7 +5235,7 @@ class Worker {
5208
5235
  const succeeded = this.runId && this.state !== "error" && !this.aborted && !this.verificationFailed;
5209
5236
  if (succeeded) {
5210
5237
  try {
5211
- await this.stateStore.endRun(this.runId, "completed");
5238
+ await this.stateStore.endRun(this.runId, "completed", this.runLedger());
5212
5239
  } catch {}
5213
5240
  await this.recordOutcome(card.id, "success");
5214
5241
  } else if (this.runId && this.timedOut) {
@@ -5234,16 +5261,25 @@ class Worker {
5234
5261
  log.error(this.tag, `timeout transition failed on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
5235
5262
  }
5236
5263
  try {
5237
- await this.stateStore.endRun(this.runId, "failed", "timeout");
5264
+ await this.stateStore.endRun(this.runId, "failed", {
5265
+ errorMessage: "timeout",
5266
+ ...this.runLedger()
5267
+ });
5238
5268
  } catch {}
5239
5269
  await this.recordOutcome(card.id, "failure");
5240
5270
  } else if (this.runId && this.aborted) {
5241
5271
  try {
5242
- await this.stateStore.endRun(this.runId, "paused", "cancelled");
5272
+ await this.stateStore.endRun(this.runId, "paused", {
5273
+ errorMessage: "cancelled",
5274
+ ...this.runLedger()
5275
+ });
5243
5276
  } catch {}
5244
5277
  } else if (this.runId && this.verificationFailed) {
5245
5278
  try {
5246
- await this.stateStore.endRun(this.runId, "paused", "verification");
5279
+ await this.stateStore.endRun(this.runId, "paused", {
5280
+ errorMessage: "verification",
5281
+ ...this.runLedger()
5282
+ });
5247
5283
  } catch {}
5248
5284
  await this.recordOutcome(card.id, "failure");
5249
5285
  }
@@ -5522,6 +5558,11 @@ class Worker {
5522
5558
  this.process.on("close", (code) => {
5523
5559
  this.process = null;
5524
5560
  this.lastSessionStats = this.progressTracker?.stats;
5561
+ const spawnCost = this.lastSessionStats?.cost;
5562
+ if (spawnCost) {
5563
+ this.runCostCents += Math.round(spawnCost.totalCostUsd * 100);
5564
+ this.runTurns += spawnCost.numTurns;
5565
+ }
5525
5566
  this.progressTracker?.flushFinal();
5526
5567
  this.progressTracker?.stop();
5527
5568
  this.progressTracker = null;
@@ -5569,6 +5610,8 @@ class Worker {
5569
5610
  this.startedAt = null;
5570
5611
  this.runId = null;
5571
5612
  this.sessionId = null;
5613
+ this.runCostCents = 0;
5614
+ this.runTurns = 0;
5572
5615
  }
5573
5616
  }
5574
5617
  var TAG23 = "worker", CANCEL_SIGINT_TIMEOUT2 = 30000, CANCEL_SIGTERM_TIMEOUT2 = 1e4, PLAN_ALLOWED_TOOLS = "Read,Grep,Glob,mcp__harmony__*", IMPLEMENT_ALLOWED_TOOLS = "Bash,Read,Write,Edit,Glob,Grep,Agent,mcp__harmony__*", PLAN_PHASE_TIMEOUT;
@@ -5791,7 +5834,8 @@ class Pool {
5791
5834
  cardId: w.cardId,
5792
5835
  cardShortId: null,
5793
5836
  startedAt: w.startedAt,
5794
- branchName: w.branchName
5837
+ branchName: w.branchName,
5838
+ costCents: w.costCents
5795
5839
  });
5796
5840
  }
5797
5841
  for (const w of this.reviewWorkers) {
@@ -5802,7 +5846,8 @@ class Pool {
5802
5846
  cardId: w.cardId,
5803
5847
  cardShortId: null,
5804
5848
  startedAt: w.startedAt,
5805
- branchName: w.branchName
5849
+ branchName: w.branchName,
5850
+ costCents: w.costCents
5806
5851
  });
5807
5852
  }
5808
5853
  return out;
@@ -6029,7 +6074,9 @@ async function recoverRun(run, store, client, config, outcome) {
6029
6074
  }
6030
6075
  }
6031
6076
  try {
6032
- await store.endRun(run.runId, "orphaned", "recovered after daemon restart");
6077
+ await store.endRun(run.runId, "orphaned", {
6078
+ errorMessage: "recovered after daemon restart"
6079
+ });
6033
6080
  } catch (err) {
6034
6081
  const msg = err instanceof Error ? err.message : String(err);
6035
6082
  outcome.errors.push(`endRun: ${msg}`);
@@ -7193,14 +7240,16 @@ function printStatus(body) {
7193
7240
  const uptime = formatDuration(body.uptimeMs);
7194
7241
  out.write(`daemon ${body.daemonId} (pid ${body.daemonPid}, up ${uptime})
7195
7242
  `);
7196
- out.write(`budget $${(body.budget.todayCents / 100).toFixed(2)} / $${(body.budget.dailyCapCents / 100).toFixed(2)} today
7243
+ const activeCents = body.workers.reduce((sum, w) => sum + (w.costCents ?? 0), 0);
7244
+ out.write(`budget $${(body.budget.todayCents / 100).toFixed(2)} / $${(body.budget.dailyCapCents / 100).toFixed(2)} today · $${(activeCents / 100).toFixed(2)} in-flight
7197
7245
  `);
7198
7246
  out.write(`workers (${body.workers.length})
7199
7247
  `);
7200
7248
  for (const w of body.workers) {
7201
7249
  const card = w.cardId ? ` card=${w.cardId}` : "";
7202
7250
  const br = w.branchName ? ` branch=${w.branchName}` : "";
7203
- out.write(` #${w.id} ${w.pipeline.padEnd(9)} ${w.state}${card}${br}
7251
+ const spend = w.costCents ? ` $${(w.costCents / 100).toFixed(2)}` : "";
7252
+ out.write(` #${w.id} ${w.pipeline.padEnd(9)} ${w.state}${card}${br}${spend}
7204
7253
  `);
7205
7254
  }
7206
7255
  out.write(`impl queue (${body.implQueue.length})
package/dist/index.js CHANGED
@@ -1558,14 +1558,6 @@ class BudgetGuard {
1558
1558
  detail: `${card.attempts} of ${this.config.maxAttemptsPerCard} attempts exhausted`
1559
1559
  };
1560
1560
  }
1561
- const dailySpent = this.store.getDailyCostCents();
1562
- if (dailySpent >= this.config.dailyBudgetCents) {
1563
- return {
1564
- allow: false,
1565
- reason: "daily_budget",
1566
- detail: `daily budget ${formatCents(dailySpent)}/${formatCents(this.config.dailyBudgetCents)} exhausted`
1567
- };
1568
- }
1569
1561
  return { allow: true };
1570
1562
  }
1571
1563
  }
@@ -1589,9 +1581,6 @@ function buildGaveUpComment(maxAttempts, failures) {
1589
1581
  return lines.join(`
1590
1582
  `);
1591
1583
  }
1592
- function formatCents(cents) {
1593
- return `$${(cents / 100).toFixed(2)}`;
1594
- }
1595
1584
 
1596
1585
  // src/error-classifier.ts
1597
1586
  function parseRetryAfterMs(message) {
@@ -2328,7 +2317,8 @@ function buildTokenPayload(stats) {
2328
2317
  outputTokens: stats.cost.totalOutputTokens,
2329
2318
  cacheCreationInputTokens: stats.cost.totalCacheCreationInputTokens,
2330
2319
  cacheReadInputTokens: stats.cost.totalCacheReadInputTokens,
2331
- modelName: stats.cost.modelName
2320
+ modelName: stats.cost.modelName,
2321
+ numTurns: stats.cost.numTurns
2332
2322
  };
2333
2323
  }
2334
2324
  async function runCompletion(client, card, branchName, worktreePath, config, workerId, sessionStats, workspaceId, agentSessionId, stateStore, onMovedToCompletion) {
@@ -2914,7 +2904,8 @@ class ProgressTracker {
2914
2904
  outputTokens: this.lastCost?.totalOutputTokens ?? 0,
2915
2905
  cacheCreationInputTokens: this.lastCost?.totalCacheCreationInputTokens ?? 0,
2916
2906
  cacheReadInputTokens: this.lastCost?.totalCacheReadInputTokens ?? 0,
2917
- modelName: this.lastCost?.modelName
2907
+ modelName: this.lastCost?.modelName,
2908
+ numTurns: this.lastCost?.numTurns ?? 0
2918
2909
  }).catch((err) => {
2919
2910
  log.warn(TAG14, `Failed to send progress update: ${err}`);
2920
2911
  });
@@ -3685,13 +3676,19 @@ class StateStore {
3685
3676
  heartbeat(runId) {
3686
3677
  return this.updateRun(runId, { lastHeartbeatAt: Date.now() });
3687
3678
  }
3688
- endRun(runId, status, errorMessage) {
3679
+ endRun(runId, status, opts = {}) {
3689
3680
  const patch = {
3690
3681
  status,
3691
3682
  endedAt: Date.now()
3692
3683
  };
3693
- if (errorMessage !== undefined)
3694
- patch.errorMessage = errorMessage;
3684
+ if (opts.errorMessage !== undefined)
3685
+ patch.errorMessage = opts.errorMessage;
3686
+ if (opts.costCents !== undefined && opts.costCents > 0) {
3687
+ patch.costCents = opts.costCents;
3688
+ }
3689
+ if (opts.numTurns !== undefined && opts.numTurns > 0) {
3690
+ patch.numTurns = opts.numTurns;
3691
+ }
3695
3692
  return this.updateRun(runId, patch);
3696
3693
  }
3697
3694
  getRun(runId) {
@@ -4123,6 +4120,19 @@ class ReviewWorker {
4123
4120
  get isActive() {
4124
4121
  return this.state === "preparing" || this.state === "running" || this.state === "verifying" || this.state === "completing";
4125
4122
  }
4123
+ get costCents() {
4124
+ const cost = (this.progressTracker?.stats ?? this.lastSessionStats)?.cost;
4125
+ return cost ? Math.round(cost.totalCostUsd * 100) : 0;
4126
+ }
4127
+ endLedger() {
4128
+ const cost = (this.lastSessionStats ?? this.progressTracker?.stats)?.cost;
4129
+ if (!cost)
4130
+ return { costCents: 0, numTurns: 0 };
4131
+ return {
4132
+ costCents: Math.round(cost.totalCostUsd * 100),
4133
+ numTurns: cost.numTurns
4134
+ };
4135
+ }
4126
4136
  async run(card, column, labels, subtasks) {
4127
4137
  this.aborted = false;
4128
4138
  this.timedOut = false;
@@ -4148,7 +4158,8 @@ class ReviewWorker {
4148
4158
  lastHeartbeatAt: this.startedAt,
4149
4159
  endedAt: null,
4150
4160
  status: "active",
4151
- costCents: 0
4161
+ costCents: 0,
4162
+ numTurns: 0
4152
4163
  });
4153
4164
  this.branchName = extractBranchFromDescription(card.description);
4154
4165
  const localMode = !this.branchName;
@@ -4319,7 +4330,7 @@ class ReviewWorker {
4319
4330
  }
4320
4331
  if (this.runId) {
4321
4332
  try {
4322
- await this.stateStore.endRun(this.runId, this.state === "error" ? "paused" : "completed");
4333
+ await this.stateStore.endRun(this.runId, this.state === "error" ? "paused" : "completed", this.endLedger());
4323
4334
  } catch {}
4324
4335
  }
4325
4336
  } finally {
@@ -4328,7 +4339,7 @@ class ReviewWorker {
4328
4339
  const run = this.stateStore.getRun(this.runId);
4329
4340
  if (run && run.endedAt === null) {
4330
4341
  const status = this.timedOut ? "failed" : this.state === "error" || this.aborted ? "paused" : "completed";
4331
- await this.stateStore.endRun(this.runId, status);
4342
+ await this.stateStore.endRun(this.runId, status, this.endLedger());
4332
4343
  }
4333
4344
  } catch {}
4334
4345
  }
@@ -4999,6 +5010,8 @@ class Worker {
4999
5010
  verificationFailed = false;
5000
5011
  sessionId = null;
5001
5012
  runId = null;
5013
+ runCostCents = 0;
5014
+ runTurns = 0;
5002
5015
  constructor(id, config, client, agentId, onDone, workspaceId, projectId, stateStore, onCardCompleted, onApiError) {
5003
5016
  this.config = config;
5004
5017
  this.client = client;
@@ -5051,10 +5064,20 @@ class Worker {
5051
5064
  get isActive() {
5052
5065
  return this.state === "preparing" || this.state === "planning" || this.state === "running" || this.state === "verifying" || this.state === "completing";
5053
5066
  }
5067
+ get costCents() {
5068
+ const live = this.progressTracker?.stats.cost;
5069
+ const liveCents = live ? Math.round(live.totalCostUsd * 100) : 0;
5070
+ return this.runCostCents + liveCents;
5071
+ }
5072
+ runLedger() {
5073
+ return { costCents: this.runCostCents, numTurns: this.runTurns };
5074
+ }
5054
5075
  async run(card, column, labels, subtasks) {
5055
5076
  this.aborted = false;
5056
5077
  this.timedOut = false;
5057
5078
  this.verificationFailed = false;
5079
+ this.runCostCents = 0;
5080
+ this.runTurns = 0;
5058
5081
  this.cardId = card.id;
5059
5082
  this.startedAt = Date.now();
5060
5083
  this.runId = newRunId();
@@ -5079,7 +5102,8 @@ class Worker {
5079
5102
  lastHeartbeatAt: this.startedAt,
5080
5103
  endedAt: null,
5081
5104
  status: "active",
5082
- costCents: 0
5105
+ costCents: 0,
5106
+ numTurns: 0
5083
5107
  });
5084
5108
  const { session } = await this.client.startAgentSession(card.id, {
5085
5109
  agentIdentifier: agentIdentifier(this.id),
@@ -5195,7 +5219,10 @@ class Worker {
5195
5219
  }
5196
5220
  if (this.runId) {
5197
5221
  try {
5198
- await this.stateStore.endRun(this.runId, "failed", errClass.kind ?? msg);
5222
+ await this.stateStore.endRun(this.runId, "failed", {
5223
+ errorMessage: errClass.kind ?? msg,
5224
+ ...this.runLedger()
5225
+ });
5199
5226
  } catch {}
5200
5227
  if (apiError) {
5201
5228
  await this.stateStore.decrementAttempt(card.id);
@@ -5207,7 +5234,7 @@ class Worker {
5207
5234
  const succeeded = this.runId && this.state !== "error" && !this.aborted && !this.verificationFailed;
5208
5235
  if (succeeded) {
5209
5236
  try {
5210
- await this.stateStore.endRun(this.runId, "completed");
5237
+ await this.stateStore.endRun(this.runId, "completed", this.runLedger());
5211
5238
  } catch {}
5212
5239
  await this.recordOutcome(card.id, "success");
5213
5240
  } else if (this.runId && this.timedOut) {
@@ -5233,16 +5260,25 @@ class Worker {
5233
5260
  log.error(this.tag, `timeout transition failed on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
5234
5261
  }
5235
5262
  try {
5236
- await this.stateStore.endRun(this.runId, "failed", "timeout");
5263
+ await this.stateStore.endRun(this.runId, "failed", {
5264
+ errorMessage: "timeout",
5265
+ ...this.runLedger()
5266
+ });
5237
5267
  } catch {}
5238
5268
  await this.recordOutcome(card.id, "failure");
5239
5269
  } else if (this.runId && this.aborted) {
5240
5270
  try {
5241
- await this.stateStore.endRun(this.runId, "paused", "cancelled");
5271
+ await this.stateStore.endRun(this.runId, "paused", {
5272
+ errorMessage: "cancelled",
5273
+ ...this.runLedger()
5274
+ });
5242
5275
  } catch {}
5243
5276
  } else if (this.runId && this.verificationFailed) {
5244
5277
  try {
5245
- await this.stateStore.endRun(this.runId, "paused", "verification");
5278
+ await this.stateStore.endRun(this.runId, "paused", {
5279
+ errorMessage: "verification",
5280
+ ...this.runLedger()
5281
+ });
5246
5282
  } catch {}
5247
5283
  await this.recordOutcome(card.id, "failure");
5248
5284
  }
@@ -5521,6 +5557,11 @@ class Worker {
5521
5557
  this.process.on("close", (code) => {
5522
5558
  this.process = null;
5523
5559
  this.lastSessionStats = this.progressTracker?.stats;
5560
+ const spawnCost = this.lastSessionStats?.cost;
5561
+ if (spawnCost) {
5562
+ this.runCostCents += Math.round(spawnCost.totalCostUsd * 100);
5563
+ this.runTurns += spawnCost.numTurns;
5564
+ }
5524
5565
  this.progressTracker?.flushFinal();
5525
5566
  this.progressTracker?.stop();
5526
5567
  this.progressTracker = null;
@@ -5568,6 +5609,8 @@ class Worker {
5568
5609
  this.startedAt = null;
5569
5610
  this.runId = null;
5570
5611
  this.sessionId = null;
5612
+ this.runCostCents = 0;
5613
+ this.runTurns = 0;
5571
5614
  }
5572
5615
  }
5573
5616
  var TAG23 = "worker", CANCEL_SIGINT_TIMEOUT2 = 30000, CANCEL_SIGTERM_TIMEOUT2 = 1e4, PLAN_ALLOWED_TOOLS = "Read,Grep,Glob,mcp__harmony__*", IMPLEMENT_ALLOWED_TOOLS = "Bash,Read,Write,Edit,Glob,Grep,Agent,mcp__harmony__*", PLAN_PHASE_TIMEOUT;
@@ -5790,7 +5833,8 @@ class Pool {
5790
5833
  cardId: w.cardId,
5791
5834
  cardShortId: null,
5792
5835
  startedAt: w.startedAt,
5793
- branchName: w.branchName
5836
+ branchName: w.branchName,
5837
+ costCents: w.costCents
5794
5838
  });
5795
5839
  }
5796
5840
  for (const w of this.reviewWorkers) {
@@ -5801,7 +5845,8 @@ class Pool {
5801
5845
  cardId: w.cardId,
5802
5846
  cardShortId: null,
5803
5847
  startedAt: w.startedAt,
5804
- branchName: w.branchName
5848
+ branchName: w.branchName,
5849
+ costCents: w.costCents
5805
5850
  });
5806
5851
  }
5807
5852
  return out;
@@ -6028,7 +6073,9 @@ async function recoverRun(run, store, client, config, outcome) {
6028
6073
  }
6029
6074
  }
6030
6075
  try {
6031
- await store.endRun(run.runId, "orphaned", "recovered after daemon restart");
6076
+ await store.endRun(run.runId, "orphaned", {
6077
+ errorMessage: "recovered after daemon restart"
6078
+ });
6032
6079
  } catch (err) {
6033
6080
  const msg = err instanceof Error ? err.message : String(err);
6034
6081
  outcome.errors.push(`endRun: ${msg}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/agent",
3
- "version": "1.10.0",
3
+ "version": "1.10.2",
4
4
  "description": "Push-based agent daemon for Harmony — watches board assignments and spawns Claude CLI workers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",