Skip to content

Commit 8993fdd

Browse files
Sync public snapshot from freebuff-private
Source: CodebuffAI/freebuff-private@2d05d07c254142741dab69c6935893d30ef57731
1 parent 95ecda2 commit 8993fdd

8 files changed

Lines changed: 96 additions & 62 deletions

File tree

‎bun.lock‎

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

‎cli/src/components/__tests__/freebuff-model-selector.test.tsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,8 +1566,9 @@ test.each([
15661566
// 48 rows: the expanded catalog outgrew 40 when Solar Pro 4 returned.
15671567
// 50 rows once FREEBUFF_ENABLE_FAST_MODE_IN_UI lists DeepSeek V4.1 Flash
15681568
// Fast: that row makes the expanded catalog two lines taller.
1569-
// Five more for the three section headers and the gaps between them.
1570-
const setup = await renderSelector(FREEBUFF_ENABLE_FAST_MODE_IN_UI ? 55 : 53, async (model) => {
1569+
// Five more for the three section headers and the gaps between them, and
1570+
// one for Solar Pro 4's "Promotional" line (2026-10-05).
1571+
const setup = await renderSelector(FREEBUFF_ENABLE_FAST_MODE_IN_UI ? 56 : 54, async (model) => {
15711572
requested.push(model)
15721573
})
15731574
expect(setup.captureCharFrame()).toContain(solarOfferAt(cutoff - 137).tagline)

‎common/src/constants/__tests__/freebuff-free-promotions.test.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ describe('free-promotion spend windows', () => {
3131
from: '2026-09-09T15:49:00.000Z',
3232
to: '2026-09-13T05:00:00.000Z',
3333
},
34+
{
35+
// Open-ended: exempt for as long as the price stays 0.
36+
modelId: FREEBUFF_SOLAR_PRO_4_MODEL_ID,
37+
from: '2026-10-05T07:15:00.000Z',
38+
to: '2099-01-01T00:00:00.000Z',
39+
},
3440
{
3541
modelId: FREEBUFF_SOLAR_MINI_4_MODEL_ID,
3642
from: '2026-10-02T18:00:00.000Z',

‎common/src/constants/freebuff-models.ts‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import {
1313
isDeepSeekExpensiveWindow,
1414
} from './freebuff-peak-hours'
1515
import { mimoModels } from './model-config'
16-
import { SOLAR_PRO_4_OFFER } from './freebuff-solar-promo'
16+
import {
17+
SOLAR_PRO_4_OFFER,
18+
SOLAR_PRO_4_PROMOTIONAL,
19+
} from './freebuff-solar-promo'
1720
import { GLM_V53_FLASH_PROMOTIONAL } from './freebuff-glm-promo'
1821
import { GPT_61_SOL_PROMOTIONAL } from './freebuff-sol-promo'
1922
import {
@@ -2522,6 +2525,9 @@ const GPT_61_SOL_MODEL = {
25222525
*
25232526
* The `supersededBy` pointer to Mini 4 went with the return: it would keep
25242527
* rewriting saved Pro 4 picks onto Mini 4 on every load.
2528+
*
2529+
* FREE AS A PROMOTION from 2026-10-05 (SOLAR_PRICE_CHANGES), open-ended, and
2530+
* listed in the pickers' Unlimited section.
25252531
*/
25262532
const SOLAR_PRO_4_MODEL = {
25272533
id: FREEBUFF_SOLAR_PRO_4_MODEL_ID,
@@ -2533,6 +2539,9 @@ const SOLAR_PRO_4_MODEL = {
25332539
dataUse: 'service',
25342540
premium: FREEBUFF_SOLAR_PRO_4_ENTITLEMENT.fullAccess.premium,
25352541
multimodal: false,
2542+
// PROMOTIONAL, by product decision (2026-10-05): 0 Freebucks is a temporary
2543+
// price. Clear it with the schedule entry that ends the promotion.
2544+
promotional: SOLAR_PRO_4_PROMOTIONAL,
25362545
} as const satisfies FreebuffModelOption
25372546

25382547
/**

‎common/src/constants/freebuff-solar-promo.ts‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ const SOLAR_MINI_4_OFFER = {
2121
tagline: 'Fast and light',
2222
} as const
2323

24+
// Solar Pro 4's promotion (2026-10-05): free, open-ended, labelled temporary
25+
// by product decision. The catalog row's `promotional` field, and the price
26+
// notice released CLI and Desktop builds render in place of the tagline.
27+
export const SOLAR_PRO_4_PROMOTIONAL = {
28+
short: 'Promotional',
29+
tooltip:
30+
'Temporary promotional price: 0 Freebucks an hour. The price will go up when the promotion ends.',
31+
} as const
32+
2433
// These transitions travel with the server quote so idle clients can update
2534
// even during a slow refresh. Preserve past prices for historical accounting.
2635
export const SOLAR_PRICE_CHANGES = [
@@ -77,6 +86,15 @@ export const SOLAR_PRICE_CHANGES = [
7786
modelId: FREEBUFF_SOLAR_MINI_4_MODEL_ID,
7887
...SOLAR_MINI_4_OFFER,
7988
},
89+
{
90+
// A promotion with no end date yet. End it by appending a transition back
91+
// to SOLAR_PRO_4_OFFER and dropping the catalog row's `promotional`; the
92+
// free window closes at that transition.
93+
at: '2026-10-05T07:15:00Z',
94+
modelId: FREEBUFF_SOLAR_PRO_4_MODEL_ID,
95+
price: 0,
96+
tagline: SOLAR_PRO_4_PROMOTIONAL.short,
97+
},
8098
] as const
8199

82100
export function solarOfferAt(

‎common/src/util/__tests__/freebuff-picker-sections.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ describe('freebuffPickerPlacement', () => {
2929
test('literal ids are the catalog constants', () => {
3030
const at = (id: string) => freebuffPickerPlacement([id], none)
3131
expect(at(FREEBUFF_SPACE_BUNNY_ALPHA_MODEL_ID)).toMatchObject({ section: 'unlimited', recommended: true })
32+
expect(at(FREEBUFF_SOLAR_PRO_4_MODEL_ID)).toEqual({ section: 'unlimited', order: expect.any(Number) })
3233
expect(at(FREEBUFF_SOLAR_MINI_4_MODEL_ID).section).toBe('unlimited')
3334
expect(at(FREEBUFF_MIMO_V25_MODEL_ID)).toMatchObject({ section: 'optimized', recommended: true })
3435
expect(at(FREEBUFF_GLM_V53_FLASH_MODEL_ID).section).toBe('optimized')
3536
expect(at(FREEBUFF_DEEPSEEK_V4_FLASH_MODEL_ID).section).toBe('optimized')
36-
expect(at(FREEBUFF_SOLAR_PRO_4_MODEL_ID)).toMatchObject({ section: 'optimized', more: true })
3737
for (const id of [
3838
FREEBUFF_MUSE_SPARK_13_CONTRIBUTOR_MODEL_ID,
3939
FREEBUFF_GPT_6_LUNA_MODEL_ID,

‎common/src/util/__tests__/freebuff-price-changes.test.ts‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const restored = Date.parse('2026-09-09T15:49:00Z')
1717
const metered = Date.parse('2026-09-13T05:00:00Z')
1818
const increased = Date.parse('2026-09-14T03:46:00Z')
1919
const returned = Date.parse('2026-09-25T19:00:00Z')
20+
const promoted = Date.parse('2026-10-05T07:15:00Z')
2021
const quoteBeforeStart = () => ({
2122
...freebucksFixture(0, { [solar]: SOLAR_REGULAR_OFFER.price }),
2223
priceNotices: { [solar]: SOLAR_REGULAR_OFFER.tagline },
@@ -54,13 +55,19 @@ describe('announced Freebucks price changes', () => {
5455
expect(increasedQuote.priceNotices[solar]).toBe('Limited-time trial')
5556
expect(nextFreebucksPriceChange(increasedQuote)).toBe(returned)
5657
// The return to the pickers keeps the price and replaces only the copy.
57-
expect(applyFreebucksPriceChanges(increasedQuote, returned)).toEqual({
58+
const returnedQuote = applyFreebucksPriceChanges(increasedQuote, returned)
59+
expect(returnedQuote).toEqual({
5860
...increasedQuote,
5961
priceNotices: { ...increasedQuote.priceNotices, [solar]: 'Upstage flagship' },
60-
priceChanges: [],
62+
priceChanges: [expect.objectContaining({ at: '2026-10-05T07:15:00Z' })],
6163
})
64+
expect(nextFreebucksPriceChange(returnedQuote)).toBe(promoted)
65+
const promotedQuote = applyFreebucksPriceChanges(returnedQuote, promoted)
66+
expect(promotedQuote.prices[solar]).toBe(0)
67+
expect(promotedQuote.priceNotices[solar]).toBe('Promotional')
68+
expect(promotedQuote.priceChanges).toEqual([])
6269
expect(quote.prices[solar]).toBe(5)
63-
expect(quote.priceChanges).toHaveLength(6)
70+
expect(quote.priceChanges).toHaveLength(7)
6471
})
6572

6673
it("applies Solar Mini 4's promotion to Mini's row only", () => {

‎common/src/util/freebuff-picker-sections.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,15 @@ const PLACEMENTS: Readonly<Record<string, FreebuffPickerPlacement>> =
5858
order: 10,
5959
recommended: true,
6060
},
61+
// Solar Pro 4 (2026-10-05): free as a promotion, moved from Optimized.
62+
'upstage/solar-pro4': { section: 'unlimited', order: 15 },
6163
'upstage/solar-mini4': { section: 'unlimited', order: 20 },
6264
'm-916b95b337': { section: 'unlimited', order: 30, more: true }, // Ling 3.1 Flash
6365
'm-a273b5e513': { section: 'unlimited', order: 40, more: true }, // Laguna S 2.1
6466
// Optimized
6567
'mimo/mimo-v2.5': { section: 'optimized', order: 10, recommended: true }, // MiMo 2.6 Flash
6668
'z-ai/glm-5.3-flash': { section: 'optimized', order: 20 },
6769
'deepseek/deepseek-v4-flash': { section: 'optimized', order: 30 }, // V4.1 Flash
68-
'upstage/solar-pro4': { section: 'optimized', order: 40, more: true },
6970
// Powerful
7071
// DeepSeek V4.1 Flash Fast (2026-10-04): the section's pick, first.
7172
'deepseek/deepseek-v4-flash-fast': {

0 commit comments

Comments
 (0)