@selesai/code 0.12.0 → 0.13.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  All notable changes to `@selesai/code` will be documented in this file.
4
4
 
5
+ ## [0.13.0] - 2026-08-29
6
+
7
+ ### Fixed
8
+ - **Sub-cent cost display.** Cost labels no longer round small per-call costs to `$0.000`. The footer, zentui footer, and usage popup now format costs adaptively: 3 decimals at $1+, 4 decimals at $0.01+, and 6 decimals below, so gateway-reconciled costs like `$0.000336` are visible.
9
+
5
10
  ## [0.12.0] - 2026-08-29
6
11
 
7
12
  ### Added
@@ -1,5 +1,7 @@
1
1
  import type { Usage } from "@earendil-works/pi-ai/compat";
2
2
  import type { SessionEntry } from "./session-manager.ts";
3
+ /** Format a USD cost with enough precision that sub-cent per-call costs don't round to $0.000. */
4
+ export declare function formatCost(cost: number): string;
3
5
  export interface UsageTotals {
4
6
  input: number;
5
7
  output: number;
@@ -1,3 +1,13 @@
1
+ /** Format a USD cost with enough precision that sub-cent per-call costs don't round to $0.000. */
2
+ export function formatCost(cost) {
3
+ if (cost === 0)
4
+ return "$0.000";
5
+ if (cost >= 1)
6
+ return `$${cost.toFixed(3)}`;
7
+ if (cost >= 0.01)
8
+ return `$${cost.toFixed(4)}`;
9
+ return `$${cost.toFixed(6)}`;
10
+ }
1
11
  export function createUsageTotals() {
2
12
  return {
3
13
  input: 0,
@@ -1,5 +1,6 @@
1
1
  import { homedir, hostname, userInfo } from "node:os";
2
2
  import type { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
3
+ import { formatCost } from "../../../../core/usage-totals.ts";
3
4
  import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
4
5
  import type {
5
6
  ColorSource,
@@ -360,7 +361,7 @@ export function buildTokenLabel(totals: UsageTotals, cacheHitIcon = "󰆼"): str
360
361
  }
361
362
 
362
363
  export function buildCostLabel(totals: UsageTotals): string {
363
- return `$${totals.cost.toFixed(3)}`;
364
+ return formatCost(totals.cost);
364
365
  }
365
366
 
366
367
  export function buildSessionDurationLabel(startEpoch: number): string {
@@ -134,7 +134,7 @@ describe("usage formatting", () => {
134
134
  const totals = getUsageTotals(ctx as never);
135
135
 
136
136
  expect(totals.cost).toBe(0.0123);
137
- expect(buildCostLabel(totals)).toBe("$0.012");
137
+ expect(buildCostLabel(totals)).toBe("$0.0123");
138
138
  });
139
139
 
140
140
  it("keeps the rate-card estimate when no reconcile entry exists", () => {
@@ -162,6 +162,20 @@ describe("usage formatting", () => {
162
162
  expect(totals.cost).toBe(0.0123);
163
163
  });
164
164
 
165
+ it("shows sub-cent reconciled costs without rounding to zero", () => {
166
+ const assistant = makeMessageUsageEntry("assistant", makeUsage(1_000, 500, 0), {
167
+ provider: "tokenin",
168
+ responseId: "chatcmpl-873",
169
+ });
170
+ const reconcile = makeReconcileEntry("tokenin", "chatcmpl-873", 0.00033601652);
171
+ const ctx = makeSessionContext([assistant, reconcile]);
172
+
173
+ const totals = getUsageTotals(ctx as never);
174
+
175
+ expect(totals.cost).toBe(0.00033601652);
176
+ expect(buildCostLabel(totals)).toBe("$0.000336");
177
+ });
178
+
165
179
  it("formats cache atoms independently and omits zero totals", () => {
166
180
  expect(buildCacheReadLabel(1_200)).toBe("R1.2k");
167
181
  expect(buildCacheWriteLabel(300)).toBe("W300");
@@ -1,7 +1,7 @@
1
1
  import { isAbsolute, relative, resolve, sep } from "node:path";
2
2
  import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
3
3
  import { areExperimentalFeaturesEnabled } from "../../../core/experimental.js";
4
- import { addUsageToTotals, createUsageTotals } from "../../../core/usage-totals.js";
4
+ import { addUsageToTotals, createUsageTotals, formatCost } from "../../../core/usage-totals.js";
5
5
  import { theme } from "../theme/theme.js";
6
6
  /**
7
7
  * Sanitize text for display in a single-line status.
@@ -127,7 +127,7 @@ export class FooterComponent {
127
127
  ? state.model.provider === "kimi-coding" || this.session.modelRuntime.isUsingOAuth(state.model.provider)
128
128
  : false;
129
129
  if (usageTotals.cost || usingSubscription) {
130
- const costStr = `$${usageTotals.cost.toFixed(3)}${usingSubscription ? " (sub)" : ""}`;
130
+ const costStr = `${formatCost(usageTotals.cost)}${usingSubscription ? " (sub)" : ""}`;
131
131
  statsParts.push(costStr);
132
132
  }
133
133
  // Colorize context percentage based on usage
@@ -25,7 +25,7 @@ import { buildGitCommandPrompt, GIT_COMMAND_MODES } from "../../core/git-command
25
25
  import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
26
26
  import { isInstallTelemetryEnabled } from "../../core/telemetry.js";
27
27
  import { hasTrustRequiringProjectResources, ProjectTrustStore } from "../../core/trust-manager.js";
28
- import { getUsageCostBreakdown } from "../../core/usage-totals.js";
28
+ import { formatCost, getUsageCostBreakdown } from "../../core/usage-totals.js";
29
29
  import { getChangelogPath, getNewEntries, normalizeChangelogLinks, parseChangelog } from "../../utils/changelog.js";
30
30
  import { copyToClipboard, readClipboardText } from "../../utils/clipboard.js";
31
31
  import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
@@ -5173,10 +5173,10 @@ export class InteractiveMode {
5173
5173
  info += `${theme.fg("dim", "Total:")} ${stats.tokens.total.toLocaleString()}\n`;
5174
5174
  if (stats.cost > 0 || cacheWaste.missedTokens > 0) {
5175
5175
  info += `\n${theme.bold("Cost")}\n`;
5176
- info += `${theme.fg("dim", "Total:")} $${stats.cost.toFixed(3)}`;
5176
+ info += `${theme.fg("dim", "Total:")} ${formatCost(stats.cost)}`;
5177
5177
  if (usageBreakdown.length > 1) {
5178
5178
  for (const entry of usageBreakdown) {
5179
- info += `\n ${theme.fg("dim", `${entry.key}:`)} $${entry.cost.toFixed(3)} ${theme.fg("dim", `(${formatTokens(entry.tokens)} tokens)`)}`;
5179
+ info += `\n ${theme.fg("dim", `${entry.key}:`)} ${formatCost(entry.cost)} ${theme.fg("dim", `(${formatTokens(entry.tokens)} tokens)`)}`;
5180
5180
  }
5181
5181
  }
5182
5182
  if (cacheWaste.missedTokens > 0) {
@@ -5184,7 +5184,7 @@ export class InteractiveMode {
5184
5184
  const detail = `${cacheWaste.missedTokens.toLocaleString()} tokens, ${missLabel}`;
5185
5185
  info +=
5186
5186
  cacheWaste.missedCost >= 0.0001
5187
- ? `\n${theme.fg("dim", "Cache Re-billed:")} $${cacheWaste.missedCost.toFixed(3)} ${theme.fg("dim", `(${detail})`)}`
5187
+ ? `\n${theme.fg("dim", "Cache Re-billed:")} ${formatCost(cacheWaste.missedCost)} ${theme.fg("dim", `(${detail})`)}`
5188
5188
  : `\n${theme.fg("dim", "Cache Re-billed:")} ${detail}`;
5189
5189
  }
5190
5190
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selesai/code",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Maintained, extension-first Pi coding agent with built-in workflows, subagents, web research, questions, skills, and an enhanced terminal UI.",
5
5
  "type": "module",
6
6
  "repository": {