Skip to content

Commit 350576c

Browse files
committed
glm 5.1 => kimi l2.5
1 parent 2f95613 commit 350576c

27 files changed

Lines changed: 614 additions & 354 deletions

agents/__tests__/editor.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ describe('editor agent', () => {
6767
expect(glmEditor.model).toBe('z-ai/glm-5.1')
6868
})
6969

70+
test('creates minimax editor', () => {
71+
const minimaxEditor = createCodeEditor({ model: 'minimax' })
72+
expect(minimaxEditor.model).toBe('minimax/minimax-m2.7')
73+
})
74+
7075
test('gpt-5 editor does not include think tags in instructions', () => {
7176
const gpt5Editor = createCodeEditor({ model: 'gpt-5' })
7277
expect(gpt5Editor.instructionsPrompt).not.toContain('<think>')
@@ -79,6 +84,12 @@ describe('editor agent', () => {
7984
expect(glmEditor.instructionsPrompt).not.toContain('</think>')
8085
})
8186

87+
test('minimax editor does not include think tags in instructions', () => {
88+
const minimaxEditor = createCodeEditor({ model: 'minimax' })
89+
expect(minimaxEditor.instructionsPrompt).not.toContain('<think>')
90+
expect(minimaxEditor.instructionsPrompt).not.toContain('</think>')
91+
})
92+
8293
test('opus editor includes think tags in instructions', () => {
8394
const opusEditor = createCodeEditor({ model: 'opus' })
8495
expect(opusEditor.instructionsPrompt).toContain('<think>')

agents/base2/base2.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,18 @@ export function createBase2(
2525
const isFree = mode === 'free' || mode === 'lite'
2626

2727
const isSonnet = false
28-
const model = isFree ? 'z-ai/glm-5.1' : 'anthropic/claude-opus-4.7'
28+
const model = isFree ? 'minimax/minimax-m2.7' : 'anthropic/claude-opus-4.7'
2929

3030
return {
3131
publisher,
3232
model,
33-
providerOptions: isFree ? {
34-
data_collection: 'deny',
35-
} : {
36-
only: ['amazon-bedrock'],
37-
},
33+
providerOptions: isFree
34+
? {
35+
data_collection: 'deny',
36+
}
37+
: {
38+
only: ['amazon-bedrock'],
39+
},
3840
displayName: 'Buffy the Orchestrator',
3941
spawnerPrompt:
4042
'Advanced base agent that orchestrates planning, editing, and reviewing for complex coding tasks',

agents/editor/editor-lite.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { createCodeEditor } from './editor'
33
import type { AgentDefinition } from '../types/agent-definition'
44

55
const definition: AgentDefinition = {
6-
...createCodeEditor({ model: 'glm' }),
6+
...createCodeEditor({ model: 'minimax' }),
77
id: 'editor-lite',
88
}
99
export default definition

agents/editor/editor.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import { publisher } from '../constants'
44
import type { AgentDefinition } from '../types/agent-definition'
55

66
export const createCodeEditor = (options: {
7-
model: 'gpt-5' | 'opus' | 'glm'
7+
model: 'gpt-5' | 'opus' | 'glm' | 'minimax'
88
}): Omit<AgentDefinition, 'id'> => {
99
const { model } = options
1010
return {
1111
publisher,
1212
model:
1313
options.model === 'gpt-5'
1414
? 'openai/gpt-5.1'
15+
: options.model === 'minimax'
16+
? 'minimax/minimax-m2.7'
1517
: options.model === 'glm'
1618
? 'z-ai/glm-5.1'
1719
: 'anthropic/claude-opus-4.7',
@@ -65,7 +67,7 @@ OR for new files or major rewrites:
6567
}
6668
</codebuff_tool_call>
6769
68-
${model === 'gpt-5' || model === 'glm'
70+
${model === 'gpt-5' || model === 'glm' || model === 'minimax'
6971
? ''
7072
: `Before you start writing your implementation, you should use <think> tags to think about the best way to implement the changes.
7173

agents/reviewer/code-reviewer-lite.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { createReviewer } from './code-reviewer'
55
const definition: SecretAgentDefinition = {
66
id: 'code-reviewer-lite',
77
publisher,
8-
...createReviewer('z-ai/glm-5.1'),
8+
...createReviewer('minimax/minimax-m2.7'),
99
}
1010

1111
export default definition

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

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@ import { useKeyboard } from '@opentui/react'
33
import React, { useCallback, useEffect, useMemo, useState } from 'react'
44

55
import { Button } from './button'
6-
import { FREEBUFF_MODELS } from '@codebuff/common/constants/freebuff-models'
6+
import {
7+
DEFAULT_FREEBUFF_MODEL_ID,
8+
FREEBUFF_DEPLOYMENT_HOURS_LABEL,
9+
FREEBUFF_MODELS,
10+
isFreebuffModelAvailable,
11+
} from '@codebuff/common/constants/freebuff-models'
712

813
import { joinFreebuffQueue } from '../hooks/use-freebuff-session'
14+
import { useNow } from '../hooks/use-now'
915
import { useFreebuffModelStore } from '../state/freebuff-model-store'
1016
import { useFreebuffSessionStore } from '../state/freebuff-session-store'
1117
import { useTerminalDimensions } from '../hooks/use-terminal-dimensions'
@@ -33,7 +39,9 @@ export const FreebuffModelSelector: React.FC = () => {
3339
const theme = useTheme()
3440
const { terminalWidth } = useTerminalDimensions()
3541
const selectedModel = useFreebuffModelStore((s) => s.selectedModel)
42+
const setSelectedModel = useFreebuffModelStore((s) => s.setSelectedModel)
3643
const session = useFreebuffSessionStore((s) => s.session)
44+
const now = useNow(60_000)
3745
const [pending, setPending] = useState<string | null>(null)
3846
const [hoveredId, setHoveredId] = useState<string | null>(null)
3947
// Keyboard cursor — separate from the actually-selected model so that
@@ -45,6 +53,15 @@ export const FreebuffModelSelector: React.FC = () => {
4553
setFocusedId(selectedModel)
4654
}, [selectedModel])
4755

56+
useEffect(() => {
57+
if (
58+
(session?.status === 'none' || !session) &&
59+
!isFreebuffModelAvailable(selectedModel, new Date(now))
60+
) {
61+
setSelectedModel(DEFAULT_FREEBUFF_MODEL_ID)
62+
}
63+
}, [now, selectedModel, session, setSelectedModel])
64+
4865
// Landing ('none'): depths come from the server snapshot, no "self" to
4966
// subtract. In-queue ('queued'): for the user's queue, "ahead" is
5067
// `position - 1` (themselves don't count); for every other queue, switching
@@ -85,7 +102,8 @@ export const FreebuffModelSelector: React.FC = () => {
85102
)
86103

87104
// Decide row vs column layout based on whether both buttons actually fit
88-
// side-by-side. Each button's inner text is "● {displayName} · {tagline} {hint}",
105+
// side-by-side. Each button's inner text is
106+
// "● {displayName} · {tagline} · {hours} {hint}",
89107
// plus 2 cols of border and 2 cols of padding. Buttons are separated by a
90108
// gap of 2. If the total exceeds the terminal width, stack vertically.
91109
const stackVertically = useMemo(() => {
@@ -97,6 +115,9 @@ export const FreebuffModelSelector: React.FC = () => {
97115
model.displayName.length +
98116
3 /* " · " */ +
99117
model.tagline.length +
118+
(model.availability === 'deployment_hours'
119+
? 3 + FREEBUFF_DEPLOYMENT_HOURS_LABEL.length
120+
: 0) +
100121
2 /* " " */ +
101122
hintWidth
102123
return sum + inner + BUTTON_CHROME + (idx > 0 ? GAP : 0)
@@ -115,10 +136,11 @@ export const FreebuffModelSelector: React.FC = () => {
115136
(modelId: string) => {
116137
if (pending) return
117138
if (modelId === committedModelId) return
139+
if (!isFreebuffModelAvailable(modelId, new Date(now))) return
118140
setPending(modelId)
119141
joinFreebuffQueue(modelId).finally(() => setPending(null))
120142
},
121-
[pending, committedModelId],
143+
[pending, committedModelId, now],
122144
)
123145

124146
// Tab / Shift+Tab and arrow keys move the focus highlight only; Enter or
@@ -136,7 +158,10 @@ export const FreebuffModelSelector: React.FC = () => {
136158
const isCommit = name === 'return' || name === 'enter' || name === 'space'
137159
if (!isForward && !isBackward && !isCommit) return
138160
if (isCommit) {
139-
if (focusedId !== committedModelId) {
161+
if (
162+
focusedId !== committedModelId &&
163+
isFreebuffModelAvailable(focusedId, new Date(now))
164+
) {
140165
key.preventDefault?.()
141166
pick(focusedId)
142167
}
@@ -154,7 +179,7 @@ export const FreebuffModelSelector: React.FC = () => {
154179
setFocusedId(target.id)
155180
}
156181
},
157-
[pending, pick, focusedId, committedModelId],
182+
[pending, pick, focusedId, committedModelId, now],
158183
),
159184
)
160185

@@ -181,15 +206,22 @@ export const FreebuffModelSelector: React.FC = () => {
181206
const isSelected = model.id === selectedModel
182207
const isHovered = hoveredId === model.id
183208
const isFocused = focusedId === model.id && !isSelected
209+
const isAvailable = isFreebuffModelAvailable(model.id, new Date(now))
184210
const indicator = isSelected ? '●' : '○'
185211
const indicatorColor = isSelected ? theme.primary : theme.muted
186-
const labelColor = isSelected ? theme.foreground : theme.muted
212+
const labelColor = isSelected && isAvailable ? theme.foreground : theme.muted
187213
// Clickable whenever picking would actually do something — i.e.
188214
// anything except re-picking the queue we're already in.
189-
const interactable = !pending && model.id !== committedModelId
215+
const interactable = !pending && isAvailable && model.id !== committedModelId
190216
const ahead = aheadByModel?.[model.id]
191217
const hint =
192-
ahead === undefined ? '' : ahead === 0 ? 'No wait' : `${ahead} ahead`
218+
!isAvailable
219+
? 'Closed'
220+
: ahead === undefined
221+
? ''
222+
: ahead === 0
223+
? 'No wait'
224+
: `${ahead} ahead`
193225

194226
const borderColor = isSelected
195227
? theme.primary
@@ -202,7 +234,7 @@ export const FreebuffModelSelector: React.FC = () => {
202234
key={model.id}
203235
onClick={() => {
204236
setFocusedId(model.id)
205-
pick(model.id)
237+
if (isAvailable) pick(model.id)
206238
}}
207239
onMouseOver={() => interactable && setHoveredId(model.id)}
208240
onMouseOut={() => setHoveredId((curr) => (curr === model.id ? null : curr))}
@@ -223,6 +255,9 @@ export const FreebuffModelSelector: React.FC = () => {
223255
{model.displayName}
224256
</span>
225257
<span fg={theme.muted}> · {model.tagline}</span>
258+
{model.availability === 'deployment_hours' && (
259+
<span fg={theme.muted}> · {FREEBUFF_DEPLOYMENT_HOURS_LABEL}</span>
260+
)}
226261
<span fg={theme.muted}> {hint.padEnd(hintWidth)}</span>
227262
</text>
228263
</Button>

cli/src/components/waiting-room-screen.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ export const WaitingRoomScreen: React.FC<WaitingRoomScreenProps> = ({
253253
⚠ Account unavailable
254254
</text>
255255
<text style={{ fg: theme.muted, wrapMode: 'word' }}>
256-
This account can't use freebuff. If you think this is a
256+
This account has been suspended and can't use freebuff. If you think this is a
257257
mistake, contact support@codebuff.com. Press Ctrl+C to exit.
258258
</text>
259259
</>

cli/src/hooks/use-freebuff-session.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { env } from '@codebuff/common/env'
2+
import { DEFAULT_FREEBUFF_MODEL_ID } from '@codebuff/common/constants/freebuff-models'
23
import { useEffect } from 'react'
34

45
import {
@@ -75,14 +76,17 @@ async function callSession(
7576
return body
7677
}
7778
}
78-
// 409 from POST means the user picked a different model than their active
79-
// session is bound to. Surface as a non-throw `model_locked` so the UI can
80-
// show a confirmation prompt (DELETE then re-POST to switch).
79+
// 409 from POST means the selected model cannot be joined right now, either
80+
// because an active session is locked to another model or because a
81+
// deployment-hours-only model is closed. Surface both as non-throw states.
8182
if (resp.status === 409 && method === 'POST') {
8283
const body = (await resp.json().catch(() => null)) as
8384
| FreebuffSessionResponse
8485
| null
85-
if (body && body.status === 'model_locked') {
86+
if (
87+
body &&
88+
(body.status === 'model_locked' || body.status === 'model_unavailable')
89+
) {
8690
return body
8791
}
8892
}
@@ -119,6 +123,7 @@ function nextDelayMs(next: FreebuffSessionResponse): number | null {
119123
case 'country_blocked':
120124
case 'banned':
121125
case 'model_locked':
126+
case 'model_unavailable':
122127
return null
123128
}
124129
}
@@ -398,6 +403,12 @@ export function useFreebuffSession(): UseFreebuffSessionResult {
398403
schedule(0)
399404
return
400405
}
406+
if (next.status === 'model_unavailable') {
407+
useFreebuffModelStore.getState().setSelectedModel(DEFAULT_FREEBUFF_MODEL_ID)
408+
nextMethod = 'GET'
409+
schedule(0)
410+
return
411+
}
401412

402413
// Startup takeover: the initial probe GET saw we already hold a seat
403414
// (from a prior CLI instance). POST now to rotate our instance id so

cli/src/state/freebuff-model-store.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {
22
DEFAULT_FREEBUFF_MODEL_ID,
3-
resolveFreebuffModel,
3+
resolveAvailableFreebuffModel,
44
} from '@codebuff/common/constants/freebuff-models'
55
import { create } from 'zustand'
66

@@ -24,11 +24,11 @@ interface FreebuffModelStore {
2424
}
2525

2626
export const useFreebuffModelStore = create<FreebuffModelStore>((set) => ({
27-
selectedModel: resolveFreebuffModel(
27+
selectedModel: resolveAvailableFreebuffModel(
2828
loadFreebuffModelPreference() ?? DEFAULT_FREEBUFF_MODEL_ID,
2929
),
3030
setSelectedModel: (model) => {
31-
const resolved = resolveFreebuffModel(model)
31+
const resolved = resolveAvailableFreebuffModel(model)
3232
saveFreebuffModelPreference(resolved)
3333
set({ selectedModel: resolved })
3434
},

cli/src/utils/local-agent-registry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ export const loadAgentDefinitions = (): AgentDefinition[] => {
370370
}
371371

372372
// Override the model of free-mode agents to match the user's pick from the
373-
// freebuff waiting room. Bundled definitions hardcode glm-5.1; we swap in
373+
// freebuff waiting room. Bundled definitions hardcode a free model; we swap in
374374
// whatever the user chose so the chat-completions request body carries the
375375
// matching model and the server-side session gate doesn't reject it as a
376376
// model mismatch.

0 commit comments

Comments
 (0)