Skip to content

Commit f3e9786

Browse files
Sync public snapshot from freebuff-private
Source: CodebuffAI/freebuff-private@b9d3f8c3096ef95a002a013b3b3a8d8777279802
1 parent e85bff0 commit f3e9786

9 files changed

Lines changed: 223 additions & 133 deletions

File tree

bun.lock

Lines changed: 26 additions & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/src/__tests__/freebuff-models.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -437,9 +437,9 @@ describe('freebuff model availability', () => {
437437
test('GLM 5.3 Flash is UNMETERED, and the two flags that say so agree', () => {
438438
// Unmetered on 2026-08-28, matching DeepSeek V4 Flash and MiMo. It was
439439
// premium-pooled while its cost was unknown; measured prod spend settled
440-
// that at $[redacted]/msg — the cheapest row we serve, 8.9x under the
441-
// already-unmetered V4 Flash. Capping the cheapest model while the dearer
442-
// ones run uncapped inverts the reason caps exist.
440+
// that as the cheapest row we serve, 8.9x under the already-unmetered
441+
// V4 Flash. Capping the cheapest model while the dearer ones run uncapped
442+
// inverts the reason caps exist.
443443
expect(
444444
getFreebuffPerModelSessionCap(FREEBUFF_GLM_V53_FLASH_MODEL_ID),
445445
).toBeUndefined()
@@ -791,7 +791,7 @@ describe('freebuff model availability', () => {
791791
test('Kimi K2.7 Code is fully removed from Freebuff', () => {
792792
// Removed 2026-07-31 (client pickers went first, on 2026-07-30). The server
793793
// half is gone too, so a stale client selection is no longer admitted —
794-
// that tail was still spending ~$[redacted]/day. Paid/BYOK Kimi is unaffected;
794+
// that tail was still a material daily spend. Paid/BYOK Kimi is unaffected;
795795
// it never resolves through these helpers.
796796
expect(SUPPORTED_FREEBUFF_MODELS.map((model) => model.id)).not.toContain(
797797
FREEBUFF_KIMI_MODEL_ID,
@@ -1235,8 +1235,8 @@ describe('freebuff model availability', () => {
12351235
})
12361236

12371237
test('MiniMax M3 is withdrawn: recognised, refused, served to nobody', () => {
1238-
// Withdrawn from free mode entirely on 2026-08-20 after reaching $[redacted]/hr.
1239-
// Out of every picker and pool...
1238+
// Withdrawn from free mode entirely on 2026-08-20 after its hourly burn
1239+
// became the largest single line on the bill. Out of every picker and pool...
12401240
expect(FREEBUFF_MODELS.map((model) => model.id)).not.toContain(
12411241
MINIMAX_M3_MODEL_ID,
12421242
)

common/src/__tests__/ox-alpha.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe('Ox Alpha is withdrawn from every surface', () => {
6262
* This test used to assert the exact opposite ("is on every surface, and
6363
* carries its CLI root with it"). It flipped because the host ended the free
6464
* promotion, not because the measurements that justified widening it were
65-
* wrong — the four days of $[redacted] traffic recorded in
65+
* wrong — the four days of $0.0000 traffic recorded in
6666
* docs/freebuff-ox-alpha.md all happened.
6767
*
6868
* What the flip costs is the thing the doc warned about in advance: on CLI

common/src/constants/free-agents.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ export const FREE_MODE_AGENT_MODELS: Record<string, Set<string>> = {
500500
// been hidden from every client picker in 75fb0ade6 (2026-07-30) while
501501
// deliberately staying valid here and in session admission, so released
502502
// clients weren't broken mid-session. That tail kept costing real spend
503-
// (~$[redacted]/day, [redacted]% of free-mode cost) because CLI builds older than 75fb0ade6
503+
// (a double-digit share of free-mode cost) because CLI builds older than 75fb0ade6
504504
// never drop a saved Kimi preference, and nothing forces those users to
505505
// upgrade. Every remaining free-mode Kimi request now 403s with
506506
// 'free_mode_invalid_agent_model'. Paid/BYOK Kimi is unaffected: the

common/src/constants/freebuff-models.ts

Lines changed: 57 additions & 70 deletions
Large diffs are not rendered by default.
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* Freebuff account standing ("Access Level") — the PRESENTATIONAL half.
3+
*
4+
* This file carries everything a client may see: the level names, their
5+
* user-facing labels and blurbs, and the wire shapes for standing info. The
6+
* actual limit matrix, thresholds and scorer live in
7+
* `./freebuff-trust.ts`, which is deliberately EXCLUDED from the public-repo
8+
* export (see scripts/public-export-manifest.txt) — a published limit is a
9+
* published target, so the numbers must never ship in a public file. Keep
10+
* that split when adding here: names, labels and shapes only, never numbers.
11+
*/
12+
13+
import type { FreebuffAccessTier } from './freebuff-models'
14+
15+
/**
16+
* Ordered least- to most-established. The order is load-bearing:
17+
* `FREEBUFF_TRUST_LEVELS.indexOf` is how "at least X" comparisons are done, so
18+
* inserting a level in the middle re-ranks every comparison in one edit rather
19+
* than requiring each call site to be found.
20+
*/
21+
export const FREEBUFF_TRUST_LEVELS = [
22+
'new',
23+
'verified',
24+
'established',
25+
'core',
26+
] as const
27+
28+
export type FreebuffTrustLevel = (typeof FREEBUFF_TRUST_LEVELS)[number]
29+
30+
/** The level an account holds before anything is known about it. Every failure
31+
* path in the resolver must land somewhere DEFINITE, and this is not it — see
32+
* `FREEBUFF_TRUST_FALLBACK_LEVEL`. */
33+
export const FREEBUFF_TRUST_MIN_LEVEL: FreebuffTrustLevel = 'new'
34+
35+
/**
36+
* The level used when signals cannot be loaded (database error, timeout).
37+
*
38+
* `established` and NOT `new`, and this is the single most consequential
39+
* constant in the file. This resolver runs on the free-mode hot path; if a
40+
* Postgres hiccup dropped every caller to `new`, one degraded dependency would
41+
* throttle the entire product to a fifth of its capacity, and it would look
42+
* exactly like an outage nobody could attribute. Failing to the level that
43+
* reproduces roughly today's flat limits means a broken resolver costs us the
44+
* enforcement, never the users. Same reasoning as the signup gate's fail-open.
45+
*/
46+
export const FREEBUFF_TRUST_FALLBACK_LEVEL: FreebuffTrustLevel = 'established'
47+
48+
export function isAtLeastTrustLevel(
49+
level: FreebuffTrustLevel,
50+
minimum: FreebuffTrustLevel,
51+
): boolean {
52+
return (
53+
FREEBUFF_TRUST_LEVELS.indexOf(level) >=
54+
FREEBUFF_TRUST_LEVELS.indexOf(minimum)
55+
)
56+
}
57+
58+
/** User-facing name. Never says "trust", "risk" or "score" — a user reading
59+
* their own level is reading an explanation of their limits, not a verdict on
60+
* their character. */
61+
export const FREEBUFF_TRUST_LEVEL_LABELS: Record<FreebuffTrustLevel, string> = {
62+
new: 'Getting started',
63+
verified: 'Verified',
64+
established: 'Established',
65+
core: 'Core member',
66+
}
67+
68+
/** One line of user-facing copy per level, shown under the label. */
69+
export const FREEBUFF_TRUST_LEVEL_BLURBS: Record<FreebuffTrustLevel, string> = {
70+
new: 'Welcome! Your account is brand new, so limits start small. They open up quickly — the steps below take a few minutes.',
71+
verified:
72+
'Your account is verified. You have solid daily limits, and a bit of history unlocks the next level.',
73+
established:
74+
'You are an established Freebuff user with generous limits on messages, spend and premium sessions.',
75+
core: 'You are a core member. You get the highest free limits we offer, in every region.',
76+
}
77+
78+
/** A signal that moved the score, in user-facing language. */
79+
export interface FreebuffTrustFactor {
80+
id: string
81+
label: string
82+
points: number
83+
}
84+
85+
/** Something the user can do to move up, with what it is worth. */
86+
export interface FreebuffTrustNextStep {
87+
id: string
88+
label: string
89+
detail: string
90+
points: number
91+
/** Where the UI should send them. Relative to the freebuff web app. */
92+
href?: string
93+
}
94+
95+
export interface FreebuffStandingHighlight {
96+
label: string
97+
value: string
98+
}
99+
100+
/**
101+
* NOTE FOR CALLERS: `highlights` is what the level WOULD grant, which is only
102+
* what the account actually gets once `FREEBUFF_TRUST_LEVELS=enforce`. Both
103+
* producers gate on that (the Earn route and the session `standing` field), so
104+
* a client that receives this can render it as fact. A third producer must do
105+
* the same — see the comment in freebuff/web/src/app/api/web/standing/route.ts
106+
* for what happens otherwise.
107+
*/
108+
export interface FreebuffStandingInfo {
109+
level: FreebuffTrustLevel
110+
label: string
111+
blurb: string
112+
score: number
113+
/** Score at which the next level starts, or null at `core`. */
114+
nextLevelAt: number | null
115+
nextLevel: FreebuffTrustLevel | null
116+
cappedBy: string | null
117+
cappedReason: string | null
118+
factors: FreebuffTrustFactor[]
119+
nextSteps: FreebuffTrustNextStep[]
120+
accessTier: FreebuffAccessTier
121+
/** Semantic, never numeric — see FreebuffStandingHighlight. */
122+
highlights: FreebuffStandingHighlight[]
123+
}

common/src/constants/freebuff-subscriptions.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ export const FREEBUFF_SUBSCRIPTION_MODEL_IDS: readonly string[] = Object.freeze(
4444
/**
4545
* The expensive half of the pool, sub-capped within each day.
4646
*
47-
* Measured 2026-08-21: Luna $[redacted] and DeepSeek V4 Pro $[redacted] per hour-session,
48-
* against Flash at $[redacted] — roughly 4-5x. Without a sub-cap a subscriber
47+
* Measured 2026-08-21: Luna and DeepSeek V4 Pro each cost roughly 4-5x Flash
48+
* per hour-session (dollar figures live in the internal cost notes, not in
49+
* this exported file). Without a sub-cap a subscriber
4950
* spending every daily session on Luna costs 5x one spending them on Flash, at
5051
* the same price, so the daily allowance would have to be priced for the worst
5152
* case and would be small for everyone.
@@ -183,8 +184,9 @@ export const FREEBUFF_SUBSCRIPTION_TIERS: readonly FreebuffSubscriptionTier[] =
183184
introPriceUsd: 5,
184185
// Resized 2026-08-27, before the public rollout. The pre-rollout
185186
// figures were sized against god-only testing and priced most of a
186-
// month of premium use into $8 — at Luna's measured $[redacted]/session,
187-
// 4/day was ~$[redacted] of compute. The 5-DAY window is what actually bounds a
187+
// month of premium use into $8 — at Luna's measured per-session cost,
188+
// 4/day was an order of magnitude more compute than the price
189+
// (figures in the internal cost notes). The 5-DAY window is what actually bounds a
188190
// heavy week (3/day would allow 15 in five days; 10 is the real cap),
189191
// which is why the two numbers are not simply proportional.
190192
dailySessions: 3,

common/src/types/freebuff-session.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { FreebuffAccessTier } from '../constants/freebuff-models'
2-
import type { FreebuffStandingInfo } from '../constants/freebuff-trust'
2+
import type { FreebuffStandingInfo } from '../constants/freebuff-standing'
33

44
/**
55
* Wire-level shapes returned by `/api/v1/freebuff/session`. Source of truth

common/src/util/freebuff-model-availability.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ export const FREEBUFF_PAUSED_MODEL_NOTICE =
5454
*
5555
* 2026-08-28: GLM 5.3 Flash is now UNMETERED, joining MiMo and DeepSeek V4
5656
* Flash. The 2-a-day cap came off on 08-27 when its measurement window closed;
57-
* a day of production spend then settled the cost question outright — it bills
58-
* $[redacted]/msg, the cheapest row we serve, 4.6x under MiMo and 8.9x under V4
57+
* a day of production spend then settled the cost question outright — it is
58+
* the cheapest row we serve per message, 4.6x under MiMo and 8.9x under V4
5959
* Flash, both of which already ran uncapped. Keeping a ceiling on the cheapest
6060
* model while the dearer ones had none inverted the reason ceilings exist.
6161
*

0 commit comments

Comments
 (0)