From 5e18ccace7d670defb9f94d5bb6b304b78c12987 Mon Sep 17 00:00:00 2001 From: Dominic O'Carroll <99632940+domocarroll@users.noreply.github.com> Date: Mon, 9 Mar 2026 14:45:09 +1000 Subject: [PATCH] fix: route heartbeat cost recording through costService Heartbeat runs recorded costs via direct SQL inserts into costEvents and agents.spentMonthlyCents, bypassing costService.createEvent(). This skipped: - companies.spentMonthlyCents update (company budget never incremented) - Agent auto-pause when budget exceeded (enforcement gap) Now calls costService(db).createEvent() which handles all three: insert cost event, update agent spend, update company spend, and auto-pause agent when budgetMonthlyCents is exceeded. --- server/src/services/heartbeat.ts | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/server/src/services/heartbeat.ts b/server/src/services/heartbeat.ts index dbba40b2..81d02137 100644 --- a/server/src/services/heartbeat.ts +++ b/server/src/services/heartbeat.ts @@ -9,7 +9,6 @@ import { agentWakeupRequests, heartbeatRunEvents, heartbeatRuns, - costEvents, issues, projectWorkspaces, } from "@paperclipai/db"; @@ -21,6 +20,7 @@ import { getServerAdapter, runningProcesses } from "../adapters/index.js"; import type { AdapterExecutionResult, AdapterInvocationMeta, AdapterSessionCodec } from "../adapters/index.js"; import { createLocalAgentJwt } from "../agent-auth-jwt.js"; import { parseObject, asBoolean, asNumber, appendWithCap, MAX_EXCERPT_BYTES } from "../adapters/utils.js"; +import { costService } from "./costs.js"; import { secretService } from "./secrets.js"; import { resolveDefaultAgentWorkspaceDir } from "../home-paths.js"; @@ -977,8 +977,8 @@ export function heartbeatService(db: Db) { .where(eq(agentRuntimeState.agentId, agent.id)); if (additionalCostCents > 0 || hasTokenUsage) { - await db.insert(costEvents).values({ - companyId: agent.companyId, + const costs = costService(db); + await costs.createEvent(agent.companyId, { agentId: agent.id, provider: result.provider ?? "unknown", model: result.model ?? "unknown", @@ -988,16 +988,6 @@ export function heartbeatService(db: Db) { occurredAt: new Date(), }); } - - if (additionalCostCents > 0) { - await db - .update(agents) - .set({ - spentMonthlyCents: sql`${agents.spentMonthlyCents} + ${additionalCostCents}`, - updatedAt: new Date(), - }) - .where(eq(agents.id, agent.id)); - } } async function startNextQueuedRunForAgent(agentId: string) {