agent-trace 0.2.3 → 0.2.5

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 (2) hide show
  1. package/agent-trace.cjs +154 -4
  2. package/package.json +1 -1
package/agent-trace.cjs CHANGED
@@ -25708,6 +25708,12 @@ function toNonNegativeInteger2(value) {
25708
25708
  }
25709
25709
  return Math.floor(value);
25710
25710
  }
25711
+ function toNonNegativeDecimal(value) {
25712
+ if (!Number.isFinite(value) || value <= 0) {
25713
+ return 0;
25714
+ }
25715
+ return Number(value.toFixed(6));
25716
+ }
25711
25717
  function toSessionStatus(trace) {
25712
25718
  return trace.endedAt !== void 0 ? "completed" : "active";
25713
25719
  }
@@ -25731,7 +25737,7 @@ function toPostgresCommitRow(trace, commit) {
25731
25737
  message: toNullableString(commit.message),
25732
25738
  lines_added: toNonNegativeInteger2(commit.linesAdded),
25733
25739
  lines_removed: toNonNegativeInteger2(commit.linesRemoved),
25734
- chain_cost_usd: 0,
25740
+ chain_cost_usd: toNonNegativeDecimal(trace.metrics.totalCostUsd),
25735
25741
  committed_at: toNullableString(commit.committedAt)
25736
25742
  };
25737
25743
  }
@@ -26013,6 +26019,109 @@ function toTimelineEventFromClickHouseRow(row) {
26013
26019
  };
26014
26020
  }
26015
26021
 
26022
+ // packages/schema/src/pricing.ts
26023
+ var MODEL_PRICING = [
26024
+ // ── Opus family ──────────────────────────────────────────
26025
+ // Opus 4.5 / 4.6 — $5 in, $25 out
26026
+ ["claude-opus-4-6", {
26027
+ inputPerToken: 5 / 1e6,
26028
+ outputPerToken: 25 / 1e6,
26029
+ cacheReadPerToken: 0.5 / 1e6,
26030
+ cacheWritePerToken: 6.25 / 1e6
26031
+ }],
26032
+ ["claude-opus-4-5", {
26033
+ inputPerToken: 5 / 1e6,
26034
+ outputPerToken: 25 / 1e6,
26035
+ cacheReadPerToken: 0.5 / 1e6,
26036
+ cacheWritePerToken: 6.25 / 1e6
26037
+ }],
26038
+ // Opus 4.1 / 4.0 — $15 in, $75 out
26039
+ ["claude-opus-4-1", {
26040
+ inputPerToken: 15 / 1e6,
26041
+ outputPerToken: 75 / 1e6,
26042
+ cacheReadPerToken: 1.5 / 1e6,
26043
+ cacheWritePerToken: 18.75 / 1e6
26044
+ }],
26045
+ ["claude-opus-4", {
26046
+ inputPerToken: 15 / 1e6,
26047
+ outputPerToken: 75 / 1e6,
26048
+ cacheReadPerToken: 1.5 / 1e6,
26049
+ cacheWritePerToken: 18.75 / 1e6
26050
+ }],
26051
+ // ── Sonnet family ────────────────────────────────────────
26052
+ // Sonnet 4.x — $3 in, $15 out
26053
+ ["claude-sonnet-4", {
26054
+ inputPerToken: 3 / 1e6,
26055
+ outputPerToken: 15 / 1e6,
26056
+ cacheReadPerToken: 0.3 / 1e6,
26057
+ cacheWritePerToken: 3.75 / 1e6
26058
+ }],
26059
+ // ── Haiku family ─────────────────────────────────────────
26060
+ // Haiku 4.5 — $1 in, $5 out
26061
+ ["claude-haiku-4", {
26062
+ inputPerToken: 1 / 1e6,
26063
+ outputPerToken: 5 / 1e6,
26064
+ cacheReadPerToken: 0.1 / 1e6,
26065
+ cacheWritePerToken: 1.25 / 1e6
26066
+ }],
26067
+ // ── Legacy 3.x models ───────────────────────────────────
26068
+ ["claude-3-5-sonnet", {
26069
+ inputPerToken: 3 / 1e6,
26070
+ outputPerToken: 15 / 1e6,
26071
+ cacheReadPerToken: 0.3 / 1e6,
26072
+ cacheWritePerToken: 3.75 / 1e6
26073
+ }],
26074
+ ["claude-3-5-haiku", {
26075
+ inputPerToken: 0.8 / 1e6,
26076
+ outputPerToken: 4 / 1e6,
26077
+ cacheReadPerToken: 0.08 / 1e6,
26078
+ cacheWritePerToken: 1 / 1e6
26079
+ }],
26080
+ ["claude-3-opus", {
26081
+ inputPerToken: 15 / 1e6,
26082
+ outputPerToken: 75 / 1e6,
26083
+ cacheReadPerToken: 1.5 / 1e6,
26084
+ cacheWritePerToken: 18.75 / 1e6
26085
+ }],
26086
+ ["claude-3-sonnet", {
26087
+ inputPerToken: 3 / 1e6,
26088
+ outputPerToken: 15 / 1e6,
26089
+ cacheReadPerToken: 0.3 / 1e6,
26090
+ cacheWritePerToken: 3.75 / 1e6
26091
+ }],
26092
+ ["claude-3-haiku", {
26093
+ inputPerToken: 0.25 / 1e6,
26094
+ outputPerToken: 1.25 / 1e6,
26095
+ cacheReadPerToken: 0.03 / 1e6,
26096
+ cacheWritePerToken: 0.3 / 1e6
26097
+ }]
26098
+ ];
26099
+ var SORTED_PRICING = [...MODEL_PRICING].sort((a, b) => b[0].length - a[0].length);
26100
+ function lookupModelPricing(model) {
26101
+ const normalized = model.toLowerCase();
26102
+ for (const [pattern, pricing] of SORTED_PRICING) {
26103
+ if (normalized.startsWith(pattern)) {
26104
+ return pricing;
26105
+ }
26106
+ }
26107
+ return void 0;
26108
+ }
26109
+ function calculateCostUsd(input) {
26110
+ if (input.model === void 0) {
26111
+ return 0;
26112
+ }
26113
+ const pricing = lookupModelPricing(input.model);
26114
+ if (pricing === void 0) {
26115
+ return 0;
26116
+ }
26117
+ const baseInput = Math.max(0, input.inputTokens);
26118
+ const output = Math.max(0, input.outputTokens);
26119
+ const cacheRead = Math.max(0, input.cacheReadTokens ?? 0);
26120
+ const cacheWrite = Math.max(0, input.cacheWriteTokens ?? 0);
26121
+ const cost = baseInput * pricing.inputPerToken + cacheRead * pricing.cacheReadPerToken + cacheWrite * pricing.cacheWritePerToken + output * pricing.outputPerToken;
26122
+ return Number(cost.toFixed(6));
26123
+ }
26124
+
26016
26125
  // packages/runtime/src/runtime.ts
26017
26126
  var import_node_http2 = __toESM(require("node:http"));
26018
26127
 
@@ -26891,6 +27000,10 @@ function buildNormalizedPayload(record, message, eventType) {
26891
27000
  if (cacheReadTokens !== void 0) {
26892
27001
  payload["cache_read_tokens"] = cacheReadTokens;
26893
27002
  }
27003
+ const cacheWriteTokens = readNumber(usage, ["cache_creation_input_tokens", "cacheCreationInputTokens"]);
27004
+ if (cacheWriteTokens !== void 0) {
27005
+ payload["cache_write_tokens"] = cacheWriteTokens;
27006
+ }
26894
27007
  const promptText = readPromptText(message);
26895
27008
  if (promptText !== void 0) {
26896
27009
  payload["prompt_text"] = promptText;
@@ -28045,11 +28158,33 @@ function buildNormalizedDetails(payload) {
28045
28158
  addCamelAlias(normalized, "file_path", "filePath");
28046
28159
  return normalized;
28047
28160
  }
28161
+ function computeEventCost(payload) {
28162
+ const explicit = readNumber3(payload, ["cost_usd", "costUsd"]);
28163
+ if (explicit !== void 0 && explicit > 0) {
28164
+ return explicit;
28165
+ }
28166
+ const model = readString4(payload, ["model"]);
28167
+ const inputTokens = readNumber3(payload, ["input_tokens", "inputTokens"]);
28168
+ const outputTokens = readNumber3(payload, ["output_tokens", "outputTokens"]);
28169
+ if (model === void 0 || inputTokens === void 0 || outputTokens === void 0) {
28170
+ return explicit;
28171
+ }
28172
+ const cacheReadTokens = readNumber3(payload, ["cache_read_tokens", "cacheReadTokens", "cache_read_input_tokens"]);
28173
+ const cacheWriteTokens = readNumber3(payload, ["cache_write_tokens", "cacheWriteTokens", "cache_creation_input_tokens"]);
28174
+ const calculated = calculateCostUsd({
28175
+ model,
28176
+ inputTokens,
28177
+ outputTokens,
28178
+ cacheReadTokens: cacheReadTokens ?? 0,
28179
+ cacheWriteTokens: cacheWriteTokens ?? 0
28180
+ });
28181
+ return calculated > 0 ? calculated : explicit;
28182
+ }
28048
28183
  function toTimelineEvent(envelope) {
28049
28184
  const payload = asRecord5(envelope.payload);
28050
28185
  const inputTokens = readNumber3(payload, ["input_tokens", "inputTokens"]);
28051
28186
  const outputTokens = readNumber3(payload, ["output_tokens", "outputTokens"]);
28052
- const costUsd = readNumber3(payload, ["cost_usd", "costUsd"]);
28187
+ const costUsd = computeEventCost(payload);
28053
28188
  const details = buildNormalizedDetails(envelope.payload);
28054
28189
  return {
28055
28190
  id: envelope.eventId,
@@ -28131,7 +28266,7 @@ function toUpdatedTrace(existing, envelope) {
28131
28266
  const mergedTimeline = [...existing.timeline, timelineEvent];
28132
28267
  const endedAt = shouldMarkEnded(envelope.eventType) ? envelope.eventTimestamp : existing.endedAt;
28133
28268
  const latestTime = endedAt ?? envelope.eventTimestamp;
28134
- const cost = readNumber3(payload, ["cost_usd", "costUsd"]) ?? 0;
28269
+ const cost = computeEventCost(payload) ?? 0;
28135
28270
  const inputTokens = readNumber3(payload, ["input_tokens", "inputTokens"]) ?? 0;
28136
28271
  const outputTokens = readNumber3(payload, ["output_tokens", "outputTokens"]) ?? 0;
28137
28272
  const linesAdded = readNumber3(payload, ["lines_added", "linesAdded"]) ?? 0;
@@ -28429,11 +28564,26 @@ function hydrateFromSqlite(runtime, sqlite, limit, eventLimit) {
28429
28564
  const extra = commits.filter((c) => !pgShas.has(c.sha));
28430
28565
  commits = [...mapped, ...extra];
28431
28566
  }
28432
- return {
28567
+ let hydratedTrace = {
28433
28568
  ...trace,
28434
28569
  timeline,
28435
28570
  git: { ...trace.git, commits }
28436
28571
  };
28572
+ if (hydratedTrace.metrics.totalCostUsd === 0 && (hydratedTrace.metrics.totalInputTokens > 0 || hydratedTrace.metrics.totalOutputTokens > 0) && hydratedTrace.metrics.modelsUsed.length > 0) {
28573
+ const model = String(hydratedTrace.metrics.modelsUsed[0]);
28574
+ const recalculated = calculateCostUsd({
28575
+ model,
28576
+ inputTokens: hydratedTrace.metrics.totalInputTokens,
28577
+ outputTokens: hydratedTrace.metrics.totalOutputTokens
28578
+ });
28579
+ if (recalculated > 0) {
28580
+ hydratedTrace = {
28581
+ ...hydratedTrace,
28582
+ metrics: { ...hydratedTrace.metrics, totalCostUsd: recalculated }
28583
+ };
28584
+ }
28585
+ }
28586
+ return hydratedTrace;
28437
28587
  });
28438
28588
  for (const trace of traces) {
28439
28589
  runtime.sessionRepository.upsert(trace);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-trace",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Self-hosted observability for AI coding agents. One command, zero config.",
5
5
  "license": "Apache-2.0",
6
6
  "bin": {