@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 +5 -0
- package/dist/core/usage-totals.d.ts +2 -0
- package/dist/core/usage-totals.js +10 -0
- package/dist/extensions/pi-zentui/extensions/zentui/format.ts +2 -1
- package/dist/extensions/pi-zentui/test/format.test.ts +15 -1
- package/dist/modes/interactive/components/footer.js +2 -2
- package/dist/modes/interactive/interactive-mode.js +4 -4
- package/package.json +1 -1
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
|
|
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.
|
|
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 =
|
|
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:")}
|
|
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}:`)}
|
|
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:")}
|
|
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.
|
|
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": {
|