Skip to content

feat(ai): show run cost and balance in credits in the response bar - #34

Merged
ndemianc merged 4 commits into
developfrom
feat/credits-display
Jul 24, 2026
Merged

feat(ai): show run cost and balance in credits in the response bar#34
ndemianc merged 4 commits into
developfrom
feat/credits-display

Conversation

@ndemianc

Copy link
Copy Markdown
Contributor

The response bar read "Opus 4.8 · $0.46 · $12.79 left"; it now reads "Opus 4.8 · 46 credits · 1,279 left". $1 = 100 credits — display only, and it matches the account dashboard exactly because the gateway already sends the same RETAIL micro-$ figures the website renders.

money() is replaced by two helpers off one conversion (MICROS_PER_CREDIT), so the editor and the website can never disagree about the same balance:

  • creditBalance() — whole credits, the number people plan around.
  • creditCost() — keeps one decimal below 10, because a cheap-model turn costs ~0.4 credits and rounding it to "0" would read as free. The old money() had the same problem and solved it with "<$0.01".

Verified against the real values behind the shipped UI: $12.79 → "1,279", $0.46 → "46", a $0.003577 gpt-oss turn → "0.4", $0.5117 Opus turn → "51", $100 → "10,000", and null/zero → "0". Gate: 22 suites, 0 failures.

The response bar read "Opus 4.8 · $0.46 · $12.79 left"; it now reads
"Opus 4.8 · 46 credits · 1,279 left". $1 = 100 credits — display only, and it
matches the account dashboard exactly because the gateway already sends the same
RETAIL micro-$ figures the website renders.

money() is replaced by two helpers off one conversion (MICROS_PER_CREDIT), so the
editor and the website can never disagree about the same balance:
- creditBalance() — whole credits, the number people plan around.
- creditCost() — keeps one decimal below 10, because a cheap-model turn costs
  ~0.4 credits and rounding it to "0" would read as free. The old money() had the
  same problem and solved it with "<$0.01".

Verified against the real values behind the shipped UI: $12.79 → "1,279",
$0.46 → "46", a $0.003577 gpt-oss turn → "0.4", $0.5117 Opus turn → "51",
$100 → "10,000", and null/zero → "0". Gate: 22 suites, 0 failures.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 24, 2026 00:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the LevelCode AI chat webview’s response/action bar to display run cost and remaining balance in credits (derived from retail micro-$), aligning the editor UI with the account dashboard’s credit-based presentation.

Changes:

  • Replaces the old money(micros) formatter with credit-based conversion/formatting helpers (MICROS_PER_CREDIT, toCredits, creditBalance, creditCost).
  • Updates the response/action bar metadata to render X credits for run cost and Y left for remaining balance.
Comments suppressed due to low confidence (1)

extensions/levelcode-ai/media/chat.html:2439

  • This comment still references "$cost"/"$left", but the action bar now displays credits. Keeping it in sync helps prevent regressions during future UI changes.
  // Response action bar: (Retry) (Copy) (Helpful) (Unhelpful) … model · $cost · $left.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread extensions/levelcode-ai/media/chat.html Outdated
Comment thread extensions/levelcode-ai/media/chat.html Outdated
Comment thread extensions/levelcode-ai/media/chat.html Outdated
…me PR #86)

creditCost() used the same unary-+ pattern the dashboard did, so it carried the
same bug: 7.0 rendered as "7", and anything under 0.05 credits collapsed to "0" —
a run that cost something reading as free.

Fixed identically to AccountPage.tsx's rate(), with an explicit "<0.1" floor, so
the editor and the website format a cost the same way rather than drifting.

Verified: 23 suites, 0 failures; $0.0002 -> "<0.1", $0.70 -> "7.0", $0.46 -> "46".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 24, 2026 01:02
…ormatters (PR #34 review)

All three review points were real.

1. creditBalance() could render a negative ("-2 left"). An overage pushes the
   remaining balance below zero in the ledger, and the dollar formatter this
   replaced clamped at $0.00 — that clamp was lost in the port. Restored.
   (The same comment also flagged creditCost() rounding sub-0.05 costs to "0";
   that half was already fixed in 7d6c711, which added the "<0.1" floor.)

2. The comment block above the helpers still described the deleted money()
   behaviour ("$0.00" / "<$0.01"), and the response-bar comment still read
   "model · $cost · $left". Both now describe credits.

3. The helpers had no tests, in a repo that already extracts and tests shipped
   chat.html functions. Added creditFormat.test.js on the narrativeUi/shHighlight
   pattern — it EXTRACTS the real functions from chat.html rather than copying
   them, and asserts MICROS_PER_CREDIT is still 10000 so the editor can't drift
   from the website's conversion. To make extraction work, toCredits and
   creditBalance became multi-line (the convention slices to "\n  }").

Verified: 23 suites, 0 failures; creditFormat is 7 cases. Both fixes are
mutation-checked — removing the clamp fails, and restoring the unary + fails.
The suite also caught an arithmetic error in my own first draft ($0.70 is 70
credits, not 7.0), which is a fair advertisement for the reviewer's point.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (5)

extensions/levelcode-ai/media/chat.html:2423

  • The comment still describes the old $ formatting ("$0.00" / "<$0.01"), but this section now formats values as credits. Updating it will prevent future confusion when editing/validating credit rendering.
  // [LevelCode] Credits are the in-product spending unit: $1 = 100 credits, so 1 credit = 10_000
  // retail micro-$. The gateway sends RETAIL micro-$ (Levelcode.retail_micros) — the same figures the

extensions/levelcode-ai/media/chat.html:2432

  • creditBalance() can render a negative value if the gateway ever reports a negative/invalid balance (previously money() clamped <=0 to "$0.00"). Clamping to >=0 keeps the UI stable and avoids showing "-1 left".
    const c = Number(micros) / MICROS_PER_CREDIT;
    return isFinite(c) ? c : 0;
  }
  // A balance reads as a whole credit count. Clamped at zero: an overage can push the remaining
  // balance negative, and "-2 left" is a worse thing to put in front of someone than "0 left" (the

extensions/levelcode-ai/media/chat.html:2443

  • This comment still references "$cost"/"$left", but the response bar now displays credits. Please update the comment so it matches the rendered UI.
    const c = toCredits(micros);

extensions/levelcode-ai/media/chat.html:2436

  • creditBalance()/creditCost() are pure formatting helpers and are easy to regress (rounding thresholds, "<0.1" behavior, etc.). Since this repo already unit-tests functions extracted from media/chat.html, please add a small test covering the examples listed in the PR description (e.g. $12.79→"1,279", $0.46→"46", $0.003577→"0.4", null/0→"0").
  // dollar formatter this replaced clamped the same way).
  function creditBalance(micros){
    const c = toCredits(micros);
    return (c > 0 ? Math.round(c) : 0).toLocaleString();

extensions/levelcode-ai/media/chat.html:2464

  • credits is still retail micro-$ (converted to credits for display), so the variable name here is misleading and can cause confusion when editing the formatter logic. Consider renaming the local parameter in addAgentDone() to something like creditsMicros and using that name throughout the function.
    const meta = document.createElement('div'); meta.className = 'agentbar-meta';

Copilot AI review requested due to automatic review settings July 24, 2026 01:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

extensions/levelcode-ai/media/chat.html:2446

  • creditCost() uses toLocaleString() without an explicit locale for costs >= 10 credits, which makes the formatting locale-dependent and can desync from the fixed comma-separated examples/tests. Using an explicit locale keeps the response bar stable across environments.
    const c = toCredits(micros);
    if (c <= 0) { return '0'; }
    if (c >= 10) { return Math.round(c).toLocaleString(); }
    return c < 0.05 ? '<0.1' : c.toFixed(1);

Comment thread extensions/levelcode-ai/media/chat.html
creditBalance/creditCost called toLocaleString() with no locale, so digit
grouping followed the machine's ambient locale. Verified: under de-DE the same
balance renders "1.279" where en-US gives "1,279".

Two consequences, both bad. The editor and the account dashboard would disagree
about the formatting of the same number, which undercuts the whole point of
converting micro-$ the same way in both places. And the new unit tests assert
comma grouping, so they were quietly dependent on the CI runner's locale — they
would have failed on a non-US machine.

Now pinned to en-US, matching the dashboard and matching the fixed US-style money
strings the bar rendered before credits. The test pulls CREDIT_LOCALE from source
and additionally fails on ANY bare toLocaleString() in chat.html, so a future
edit cannot reintroduce the ambient-locale dependency.

Verified: 23 suites, 0 failures; creditFormat is 8 cases. Mutation-checked —
reverting to a bare toLocaleString() fails the suite.

Note: the account dashboard (onetime AccountPage.tsx) has the same bare call.
That PR is already merged, so it needs its own follow-up rather than a change
here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 24, 2026 01:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

extensions/levelcode-ai/media/chat.html:2459

  • In addAgentDone(), the parameter named credits actually carries the remaining balance in retail micro-$ (per the comment below and the agentDone message field), not a credit count. Now that the UI renders credits, this name is especially misleading—rename the parameter to reflect its unit (e.g. creditsMicros) to avoid future mixups.
  function addAgentDone(reason, edits, credits, maxSteps, costMicros){
    closeGroup();   // the run is over — finalize any open activity group
    const bar = document.createElement('div'); bar.className = 'agentbar';
    // Stamp the model that produced THIS turn, so a later reaction is attributed to it
    // (not whatever the plan is entitled to at click time — models can change mid-session).

@ndemianc
ndemianc merged commit d39ea06 into develop Jul 24, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants