Skip to content

Commit 0faee3d

Browse files
committed
Add Gemini Pro freebuff model
1 parent 3e1ffc2 commit 0faee3d

13 files changed

Lines changed: 356 additions & 40 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { createBase2 } from './base2'
2+
3+
const definition = {
4+
...createBase2('free', {
5+
noAskUser: true,
6+
model: 'google/gemini-3.1-pro-preview',
7+
providerOptions: {},
8+
}),
9+
id: 'base2-gemini-no-editor-evals',
10+
displayName: 'Buffy the Gemini Evals Orchestrator',
11+
}
12+
13+
export default definition

agents/base2/base2.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,37 @@ export function createBase2(
1212
hasNoValidation?: boolean
1313
planOnly?: boolean
1414
noAskUser?: boolean
15+
model?: SecretAgentDefinition['model']
16+
providerOptions?: SecretAgentDefinition['providerOptions']
1517
},
1618
): Omit<SecretAgentDefinition, 'id'> {
1719
const {
1820
hasNoValidation = mode === 'fast',
1921
planOnly = false,
2022
noAskUser = false,
23+
model: modelOverride,
24+
providerOptions,
2125
} = options ?? {}
2226
const isDefault = mode === 'default'
2327
const isFast = mode === 'fast'
2428
const isMax = mode === 'max'
2529
const isFree = mode === 'free' || mode === 'lite'
2630

2731
const isSonnet = false
28-
const model = isFree ? 'z-ai/glm-5.1' : 'anthropic/claude-opus-4.7'
32+
const model =
33+
modelOverride ?? (isFree ? 'z-ai/glm-5.1' : 'anthropic/claude-opus-4.7')
34+
const defaultProviderOptions = isFree
35+
? {
36+
data_collection: 'deny' as const,
37+
}
38+
: {
39+
only: ['amazon-bedrock'],
40+
}
2941

3042
return {
3143
publisher,
3244
model,
33-
providerOptions: isFree ? {
34-
data_collection: 'deny',
35-
} : {
36-
only: ['amazon-bedrock'],
37-
},
45+
providerOptions: providerOptions ?? defaultProviderOptions,
3846
displayName: 'Buffy the Orchestrator',
3947
spawnerPrompt:
4048
'Advanced base agent that orchestrates planning, editing, and reviewing for complex coding tasks',
@@ -150,8 +158,6 @@ Use the spawn_agents tool to spawn specialized agents to help you complete the u
150158
isMax &&
151159
`- IMPORTANT: You must spawn the editor-multi-prompt agent to implement the changes after you have gathered all the context you need. You must spawn this agent for non-trivial changes, since it writes much better code than you would with the str_replace or write_file tools. Don't spawn the editor in parallel with context-gathering agents.`,
152160
isFree &&
153-
'- Implement code changes using the str_replace or write_file tools directly.',
154-
isFree &&
155161
'- Spawn a code-reviewer-lite to review the changes after you have implemented the changes.',
156162
'- Spawn bashers sequentially if the second command depends on the the first.',
157163
isDefault &&

cli/src/components/freebuff-model-selector.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'
55
import { Button } from './button'
66
import {
77
FALLBACK_FREEBUFF_MODEL_ID,
8+
FREEBUFF_GEMINI_PRO_MODEL_ID,
89
FREEBUFF_GLM_MODEL_ID,
910
FREEBUFF_MODELS,
1011
getFreebuffDeploymentAvailabilityLabel,
@@ -25,8 +26,15 @@ import {
2526
import type { KeyEvent } from '@opentui/core'
2627

2728
const FREEBUFF_MODEL_SELECTOR_MODELS = [
29+
...FREEBUFF_MODELS.filter(
30+
(model) => model.id === FREEBUFF_GEMINI_PRO_MODEL_ID,
31+
),
2832
...FREEBUFF_MODELS.filter((model) => model.id === FREEBUFF_GLM_MODEL_ID),
29-
...FREEBUFF_MODELS.filter((model) => model.id !== FREEBUFF_GLM_MODEL_ID),
33+
...FREEBUFF_MODELS.filter(
34+
(model) =>
35+
model.id !== FREEBUFF_GEMINI_PRO_MODEL_ID &&
36+
model.id !== FREEBUFF_GLM_MODEL_ID,
37+
),
3038
]
3139

3240
/**

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
11
import { describe, expect, test } from 'bun:test'
22

33
import {
4+
FREEBUFF_GEMINI_PRO_MODEL_ID,
5+
FREEBUFF_MODELS,
46
getFreebuffDeploymentAvailabilityLabel,
57
isFreebuffDeploymentHours,
8+
isFreebuffModelAvailable,
69
} from '../constants/freebuff-models'
710

811
describe('freebuff model availability', () => {
12+
test('includes Gemini 3.1 Pro as an always-available option', () => {
13+
expect(FREEBUFF_MODELS.map((model) => model.id)).toContain(
14+
FREEBUFF_GEMINI_PRO_MODEL_ID,
15+
)
16+
expect(
17+
isFreebuffModelAvailable(
18+
FREEBUFF_GEMINI_PRO_MODEL_ID,
19+
new Date('2026-01-05T18:00:00Z'),
20+
),
21+
).toBe(true)
22+
expect(
23+
isFreebuffModelAvailable(
24+
FREEBUFF_GEMINI_PRO_MODEL_ID,
25+
new Date('2026-01-05T12:00:00Z'),
26+
),
27+
).toBe(true)
28+
})
29+
930
test('formats the close time in the user local timezone while deployment is open', () => {
1031
expect(
1132
getFreebuffDeploymentAvailabilityLabel(new Date('2026-01-05T18:00:00Z'), {

common/src/constants/free-agents.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { parseAgentId } from '../util/agent-id-parsing'
22

3+
import { FREEBUFF_MODELS } from './freebuff-models'
4+
35
import type { CostMode } from './model-config'
46

57
/**
@@ -15,6 +17,10 @@ export const FREE_COST_MODE = 'free' as const
1517
* every user's apparent activity.
1618
*/
1719
export const FREEBUFF_ROOT_AGENT_IDS = ['base2-free'] as const
20+
const FREEBUFF_ROOT_AGENT_ID_SET: ReadonlySet<string> = new Set(
21+
FREEBUFF_ROOT_AGENT_IDS,
22+
)
23+
const FREEBUFF_SELECTABLE_MODEL_IDS = FREEBUFF_MODELS.map((model) => model.id)
1824

1925
/**
2026
* Agents that are allowed to run in FREE mode.
@@ -26,10 +32,7 @@ export const FREEBUFF_ROOT_AGENT_IDS = ['base2-free'] as const
2632
*/
2733
export const FREE_MODE_AGENT_MODELS: Record<string, Set<string>> = {
2834
// Root orchestrator
29-
'base2-free': new Set([
30-
'minimax/minimax-m2.7',
31-
'z-ai/glm-5.1',
32-
]),
35+
'base2-free': new Set(FREEBUFF_SELECTABLE_MODEL_IDS),
3336

3437
// File exploration agents
3538
'file-picker': new Set(['google/gemini-2.5-flash-lite']),
@@ -44,16 +47,10 @@ export const FREE_MODE_AGENT_MODELS: Record<string, Set<string>> = {
4447
'basher': new Set(['google/gemini-3.1-flash-lite-preview']),
4548

4649
// Editor for free mode
47-
'editor-lite': new Set([
48-
'minimax/minimax-m2.7',
49-
'z-ai/glm-5.1',
50-
]),
50+
'editor-lite': new Set(FREEBUFF_SELECTABLE_MODEL_IDS),
5151

5252
// Code reviewer for free mode
53-
'code-reviewer-lite': new Set([
54-
'minimax/minimax-m2.7',
55-
'z-ai/glm-5.1',
56-
]),
53+
'code-reviewer-lite': new Set(FREEBUFF_SELECTABLE_MODEL_IDS),
5754
}
5855

5956
/**
@@ -87,6 +84,13 @@ export function isFreeMode(costMode: CostMode | string | undefined): boolean {
8784
return costMode === FREE_COST_MODE
8885
}
8986

87+
export function isFreebuffRootAgent(fullAgentId: string): boolean {
88+
const { publisherId, agentId } = parseAgentId(fullAgentId)
89+
if (!agentId) return false
90+
if (publisherId && publisherId !== 'codebuff') return false
91+
return FREEBUFF_ROOT_AGENT_ID_SET.has(agentId)
92+
}
93+
9094
/**
9195
* Check if a specific agent is allowed to use a specific model in FREE mode.
9296
* This is the strictest check - validates both the agent AND model combination.

common/src/constants/freebuff-models.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface FreebuffModelOption {
2121
* the caller's local timezone. The CLI should render
2222
* `getFreebuffDeploymentAvailabilityLabel()` instead. */
2323
export const FREEBUFF_DEPLOYMENT_HOURS_LABEL = '9am ET-5pm PT every day'
24+
export const FREEBUFF_GEMINI_PRO_MODEL_ID = 'google/gemini-3.1-pro-preview'
2425
export const FREEBUFF_GLM_MODEL_ID = 'z-ai/glm-5.1'
2526
export const FREEBUFF_MINIMAX_MODEL_ID = 'minimax/minimax-m2.7'
2627
const FREEBUFF_EASTERN_TIMEZONE = 'America/New_York'
@@ -40,6 +41,12 @@ interface LocalTimeFormatOptions {
4041
}
4142

4243
export const FREEBUFF_MODELS = [
44+
{
45+
id: FREEBUFF_GEMINI_PRO_MODEL_ID,
46+
displayName: 'Gemini 3.1 Pro',
47+
tagline: 'Deepest, 1/day',
48+
availability: 'always',
49+
},
4350
{
4451
id: FREEBUFF_MINIMAX_MODEL_ID,
4552
displayName: 'MiniMax M2.7',

common/src/types/contracts/database.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type GetUserInfoFromApiKeyFn = <T extends UserColumn>(
3535

3636
type AgentRun = {
3737
agent_id: string
38+
ancestor_run_ids: string[]
3839
status: 'running' | 'completed' | 'failed' | 'cancelled'
3940
}
4041
export type AgentRunColumn = keyof AgentRun

common/src/types/freebuff-session.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@
99
/**
1010
* Per-model usage counter surfaced to the CLI so the waiting-room UI can
1111
* render "N of M sessions used" alongside queue/active state. Present when
12-
* the joined model has a rate limit applied (today: GLM 5.1 with 5 admits
13-
* per 12-hour window). `recentCount` is the number of admissions inside
14-
* `windowHours` at the time the response was produced — see also the
15-
* standalone `rate_limited` status for the reject path.
12+
* the joined model has a rate limit applied. `recentCount` is the number of
13+
* admissions inside `windowHours` at the time the response was produced —
14+
* see also the standalone `rate_limited` status for the reject path.
1615
*/
1716
export interface FreebuffSessionRateLimit {
1817
model: string
@@ -72,7 +71,7 @@ export type FreebuffSessionServerResponse =
7271
queueDepthByModel: Record<string, number>
7372
estimatedWaitMs: number
7473
queuedAt: string
75-
/** Rate-limit quota for rate-limited models (GLM 5.1 today). Absent
74+
/** Rate-limit quota for rate-limited models. Absent
7675
* for unlimited models or when the status was produced outside the
7776
* rate-limit check path (e.g. pure read via GET). */
7877
rateLimit?: FreebuffSessionRateLimit
@@ -85,7 +84,7 @@ export type FreebuffSessionServerResponse =
8584
admittedAt: string
8685
expiresAt: string
8786
remainingMs: number
88-
/** Rate-limit quota for rate-limited models (GLM 5.1 today). Absent
87+
/** Rate-limit quota for rate-limited models. Absent
8988
* for unlimited models or when the status was produced outside the
9089
* rate-limit check path (e.g. pure read via GET). */
9190
rateLimit?: FreebuffSessionRateLimit
@@ -152,7 +151,7 @@ export type FreebuffSessionServerResponse =
152151
}
153152
| {
154153
/** User has used up their per-model admission quota in the rolling
155-
* window (GLM 5.1: 5 one-hour sessions per 12h). Returned from POST
154+
* window. Returned from POST
156155
* /session before the user is placed in the queue. `retryAfterMs` is
157156
* the time until the oldest admission inside the window falls off
158157
* and one quota slot opens up — clients should show the user when

0 commit comments

Comments
 (0)