@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.
- package/dist/cli.js +79 -30
- package/dist/index.js +75 -28
- 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,
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
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}`);
|