fix(mcp): real quota display (% + $), billing-aware unlimited, quota-not-credits enrichment#152
Conversation
…imited billing-aware leadbay_account_status described quota in raw 'credits'. Now it mirrors the web app: a % used + $ spend gauge per daily/weekly/monthly window (from quota_status spend[]), with a per-resource breakdown fallback when no spend cap exists. Quota is included on any account-status question when readable. Also fixes the unlimited-account rule: an @leadbay.ai account is unlimited ONLY when billing is disabled (backend seat sentinel 100000, or billing absent). @leadbay.ai with billing ENABLED (metered freemium/paid) is now treated as a real user — real quota gauge, real credit gate. Co-Authored-By: Claude <noreply@anthropic.com>
After a quota-consuming action completes (bulk enrich all_done, enrich_contacts) or a confirmed top-up, the agent now calls leadbay_account_status and shows the per-window %/$ quota gauge — the canonical surface — instead of only a bare credits line. Skipped for unlimited accounts, unreadable quota, or no billing. Co-Authored-By: Claude <noreply@anthropic.com>
…channel
The client pre-refused enrichment when billing.ai_credits <= 0, but ai_credits
is credits CONSUMED (an accumulator starting at 0) — so freemium/fresh accounts
with quota still available were falsely blocked ('No enrichment credits
remaining'). Removed the client-side gate; the backend 429 is the real quota
gate. Reframed enrich prompts to talk QUOTA (email + phone reveals consume
quota), dropping the confusing 'credits' framing and the fabricated 'You have N
credits' line. When the user asks to enrich without naming a channel, the agent
now asks via ask_user_input_v0 whether to add phone (email is the default).
Superseded the obsolete credit-gate tests (enrich-contacts.test.ts rewritten;
enrich-contacts-unlimited.test.ts → enrich-contacts-quota-gate.test.ts).
Co-Authored-By: Claude <noreply@anthropic.com>
The runtime elicitation prompt (shown directly by the host on an elicitation-capable path) said 'This spends credits (balance: N)' — bypassing the template reframe. Rewrote it to talk quota (email/phone reveals consume quota) and reframed the credits_remaining output-schema description to advisory-only (never a spend gate). Addresses Codex review on #152. Co-Authored-By: Claude <noreply@anthropic.com>
…quota-understanding-in-the-mcp # Conflicts: # packages/core/src/tool-descriptions.generated.ts # packages/promptforge/tool-descriptions/composite/bulk-enrich-status.md.tmpl
…NS_EXTRA_REFILL The new quota guidance preferred quota.user and referred to the resource as lowercase lens_extra_refill. But leadbay_extend_lens pre-checks are ORG-scoped (extend-lens.ts reads quota.org.resources) and match the uppercase LENS_EXTRA_REFILL. Carved out the pre-check: user preference stays for the display gauge only; the LENS_EXTRA_REFILL pre-check reads quota.org.resources[] and matches case-insensitively (both casings). Addresses Codex review on #152. Co-Authored-By: Claude <noreply@anthropic.com>
…untime lookup readExtraRefillQuota() matched the resource type with exact-case === "LENS_EXTRA_REFILL", but the live quota API emits lowercase lens_extra_refill — so a 429 from leadbay_extend_lens nulled out used_today / resets_at even though the backend supplied the row. Now normalizes case (toUpperCase compare), matching the lowercase shape this PR documents as valid. New test file covers the lowercase + mixed-case rows. Addresses Codex review on #152. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e4446b4486
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
1. Preserve unlimited when seats absent: @leadbay.ai + billing present but no seats is now treated as unknown/legacy-disabled (unlimited), not metered — fixes a product#3851 regression for the pre-3865 internal shape. 2. Granular enrich: credits_remaining is advisory/non-displayable — never print _(N credits remaining)_ (0 falsely reads as out-of-credits on quota-gated). 3. Post-spend refresh: leadbay_enrich_contacts only LAUNCHES async work, so defer the quota refresh to bulk all_done / get_contacts enrichment.done — not immediately after the granular launch. 4. Keep tests in new files: restored enrich-contacts.test.ts to main minus the obsolete throw test; new quota-gate coverage lives in the new file. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0e87a0e3ea
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
1. bulk-enrich-status: credits_remaining is advisory/non-displayable — stop allowing _(N credits remaining)_ (a consumed counter reads 0 on a quota-backed account, falsely 'out of credits'). account_status gauge is the only quota surface. 2. enrich-titles no-channel elicitation now offers a phone opt-in (include_phone toggle) instead of silently confirming email-only, honoring the promised 'email vs email + phone' choice. New test file covers it. 3. lens-refill pre-check falls back to quota.user.resources[] when quota.org is absent (non-admin callers only get the user group) — else the row is invisible and the write burns the 429 the pre-check exists to avoid. Applied in the snippet, template, account-status schema, and extend-lens runtime. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b8adb6406
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
1. bulk-enrich-status OUTPUT SCHEMA: credits_remaining reframed advisory/ non-displayable (the template was fixed earlier; the schema still called it a balance — MCP clients see both). 2. Direct extend-lens prompts (extend-lens.md.tmpl + leadbay_extend_my_lens): lens-refill pre-check now org-first then user-fallback + case-insensitive, matching the account-status guidance. 3. WORKFLOWS.md 34 & 42: drop the 'surface credits_remaining' requirement — surface enrichable_contacts only; enrichment consumes quota, credits not displayed. Keeps the normative contract aligned with the prompt change. 4. get-quota template/description: prefer user group (org admin-only), describe spend[] %/$ gauge + resources[] + topup, case-insensitive types, no 'credits'. 5. account-status rendering_hint: prefer quota.user, fall back to quota.org when user absent (admins/legacy org-only shapes keep their display). Co-Authored-By: Claude <noreply@anthropic.com>
What & why
The MCP talked in raw "credits" for quota, and a freemium user with quota left was wrongly told they couldn't enrich. This unifies the full fix for #3865 (folds in the enrichment work from the former #153).
1. Quota renders like the frontend.
leadbay_account_statusshows a per-window % used + $ spend gauge (daily/weekly/monthly, fromquota_statusspend[]), with a per-resource breakdown fallback when the plan has no spend cap. Included on any account question (incl. "what account am I connected to?"); silent only when unreadable, unlimited, or null.2. Billing-aware unlimited.
@leadbay.aiis unlimited only when billing is disabled (seat sentinel100000, or billing absent). Metered freemium/paid@leadbay.aiis treated as a real user — real gauge, real gate.3. Enrichment is gated by quota, not credits. Removed the buggy client-side
ai_credits <= 0refusal (ai_creditsis credits CONSUMED, starts at 0 — it falsely blocked freemium accounts). The backend 429 is the real gate. Reframed all enrich prompts + the runtime elicitation copy from "credits" to quota (email + phone reveals consume quota). "Enrich" with no channel → the agent asks email-only vs email+phone viaask_user_input_v0.4. Refreshed quota after a paid action. After a completed enrichment or a confirmed top-up, the agent shows the refreshed per-window gauge.
Changes
rendering/quota-windows.md(new) +account-status.md.tmpl— the gauge render._credits-helpers.tsbilling-awareisUnlimitedAccount;types.ts—billing.seats,QuotaResourceUsage.max_units,topup, nullableplan/org.tools/enrich-contacts.ts— dropped the credit gate; advisory read only.enrich-titles.tsruntime elicitation — quota-not-credits + ask-about-phone.server-instructions/quota-topup.md+bulk-enrich-status.md.tmpl— show-quota-after-spend.account-status-quota-render.test.ts,unlimited-billing-aware.test.ts,enrich-contacts-quota-gate.test.ts. Superseded the obsolete credit-gate contract by removingunlimited-account.test.ts+enrich-contacts-unlimited.test.tsand rewritingenrich-contacts.test.ts(delete+rewrite, per repo owner's explicit direction — the obsolete assertions tested the exact bug being fixed; all still-valid coverage carried over).Verification
pnpm -r build && pnpm -r test && pnpm -r typecheck— green (core 539, mcp 525, promptforge 16, components 47).@leadbay.aifreemium now returnsunlimited_credits: false+ populatedspend[]; the gauge renders; enrichment no longer false-refuses.No eval run (author-run; prompt-behaviour change). WORKFLOW 30 (silent-on-unreadable-quota) stays green.
Closes https://github.com/leadbay/product/issues/3865