Skip to content

Commit f61797e

Browse files
committed
Merge remote-tracking branch 'origin/main' into canopy-wave-kimi
2 parents f86be95 + c68b19d commit f61797e

62 files changed

Lines changed: 5883 additions & 763 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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(kimiEditor.model).toBe('moonshotai/kimi-k2.6')
6868
})
6969

70+
test('creates kimi editor', () => {
71+
const kimiEditor = createCodeEditor({ model: 'kimi' })
72+
expect(kimiEditor.model).toBe('moonshotai/kimi-k2.6')
73+
})
74+
7075
test('creates minimax editor', () => {
7176
const minimaxEditor = createCodeEditor({ model: 'minimax' })
7277
expect(minimaxEditor.model).toBe('minimax/minimax-m2.7')
@@ -84,6 +89,12 @@ describe('editor agent', () => {
8489
expect(kimiEditor.instructionsPrompt).not.toContain('</think>')
8590
})
8691

92+
test('kimi editor does not include think tags in instructions', () => {
93+
const kimiEditor = createCodeEditor({ model: 'kimi' })
94+
expect(kimiEditor.instructionsPrompt).not.toContain('<think>')
95+
expect(kimiEditor.instructionsPrompt).not.toContain('</think>')
96+
})
97+
8798
test('minimax editor does not include think tags in instructions', () => {
8899
const minimaxEditor = createCodeEditor({ model: 'minimax' })
89100
expect(minimaxEditor.instructionsPrompt).not.toContain('<think>')

agents/editor/editor.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
21
import { publisher } from '../constants'
32

43
import type { AgentDefinition } from '../types/agent-definition'
54

65
export const createCodeEditor = (options: {
7-
model: 'gpt-5' | 'opus' | 'kimi' | 'minimax'
6+
model: 'gpt-5' | 'opus' | 'glm' | 'kimi' | 'minimax'
87
}): Omit<AgentDefinition, 'id'> => {
98
const { model } = options
109
return {
@@ -16,6 +15,8 @@ export const createCodeEditor = (options: {
1615
? 'minimax/minimax-m2.7'
1716
: options.model === 'kimi'
1817
? 'moonshotai/kimi-k2.6'
18+
: options.model === 'glm'
19+
? 'z-ai/glm-5.1'
1920
: 'anthropic/claude-opus-4.7',
2021
...(options.model === 'opus' && {
2122
providerOptions: {
@@ -32,7 +33,7 @@ export const createCodeEditor = (options: {
3233
inheritParentSystemPrompt: true,
3334

3435
instructionsPrompt: `You are an expert code editor with deep understanding of software engineering principles. You were spawned to generate an implementation for the user's request. Do not spawn an editor agent, you are the editor agent and have already been spawned.
35-
36+
3637
Your task is to write out ALL the code changes needed to complete the user's request in a single comprehensive response.
3738
3839
Important: You can not make any other tool calls besides editing files. You cannot read more files, write todos, spawn agents, or set output. set_output in particular should not be used. Do not call any of these tools!
@@ -67,7 +68,7 @@ OR for new files or major rewrites:
6768
}
6869
</codebuff_tool_call>
6970
70-
${model === 'gpt-5' || model === 'kimi' || model === 'minimax'
71+
${model === 'gpt-5' || model === 'glm' || model === 'kimi' || model === 'minimax'
7172
? ''
7273
: `Before you start writing your implementation, you should use <think> tags to think about the best way to implement the changes.
7374

agents/types/agent-definition.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ export type ModelName =
423423
// Other open source models
424424
| 'moonshotai/kimi-k2'
425425
| 'moonshotai/kimi-k2:nitro'
426+
| 'moonshotai/kimi-k2.6'
426427
| 'z-ai/glm-5'
427428
| 'z-ai/glm-5.1'
428429
| 'z-ai/glm-4.6'

cli/src/chat.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ export const Chat = ({
174174
})
175175
const hasSubscription = subscriptionData?.hasSubscription ?? false
176176

177-
const { adData, recordImpression } = useGravityAd({
177+
const { ads, recordImpression } = useGravityAd({
178178
enabled: IS_FREEBUFF || !hasSubscription,
179179
provider: 'gravity',
180180
fallbackProvider: 'carbon',
@@ -1463,11 +1463,8 @@ export const Chat = ({
14631463
/>
14641464
)}
14651465

1466-
{adData && (IS_FREEBUFF || getAdsEnabled()) && (
1467-
<ChoiceAdBanner
1468-
ads={adData.variant === 'choice' ? adData.ads : [adData.ad]}
1469-
onImpression={recordImpression}
1470-
/>
1466+
{ads && (IS_FREEBUFF || getAdsEnabled()) && (
1467+
<ChoiceAdBanner ads={ads} onImpression={recordImpression} />
14711468
)}
14721469

14731470
{reviewMode ? (

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export const FreebuffModelSelector: React.FC = () => {
7272
// unavailable (e.g. deployment hours close while the picker is open),
7373
// swap to the always-available fallback so Enter doesn't POST a model
7474
// the server will immediately reject. In-memory only — the user's saved
75-
// preference (e.g. GLM) is preserved for the next launch.
75+
// preference (e.g. Kimi) is preserved for the next launch.
7676
if (
7777
(session?.status === 'none' || !session) &&
7878
!isFreebuffModelAvailable(selectedModel, new Date(now))

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

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { exitFreebuffCleanly } from '../utils/freebuff-exit'
1717
import { getLogoAccentColor, getLogoBlockColor } from '../utils/theme-system'
1818

1919
import type { FreebuffSessionResponse } from '../types/freebuff-session'
20+
import type { FreebuffIpPrivacySignal } from '@codebuff/common/types/freebuff-session'
2021

2122
interface WaitingRoomScreenProps {
2223
session: FreebuffSessionResponse | null
@@ -55,6 +56,35 @@ const formatRetryAfter = (ms: number): string => {
5556
return rem === 0 ? `${hours}h` : `${hours}h ${rem}m`
5657
}
5758

59+
const PRIVACY_SIGNAL_LABELS: Partial<Record<FreebuffIpPrivacySignal, string>> =
60+
{
61+
anonymous: 'anonymized network',
62+
proxy: 'proxy',
63+
relay: 'relay',
64+
res_proxy: 'residential proxy',
65+
tor: 'Tor',
66+
vpn: 'VPN',
67+
}
68+
69+
const formatPrivacySignalList = (
70+
signals: FreebuffIpPrivacySignal[] | undefined,
71+
): string => {
72+
const labels = Array.from(
73+
new Set(
74+
signals
75+
?.map((signal) => PRIVACY_SIGNAL_LABELS[signal])
76+
.filter((label): label is string => Boolean(label)) ?? [],
77+
),
78+
)
79+
80+
if (labels.length === 0) {
81+
return 'VPN, Tor, proxy, relay, or anonymized network'
82+
}
83+
if (labels.length === 1) return labels[0]
84+
if (labels.length === 2) return `${labels[0]} or ${labels[1]}`
85+
return `${labels.slice(0, -1).join(', ')}, or ${labels[labels.length - 1]}`
86+
}
87+
5888
export const WaitingRoomScreen: React.FC<WaitingRoomScreenProps> = ({
5989
session,
6090
error,
@@ -85,7 +115,7 @@ export const WaitingRoomScreen: React.FC<WaitingRoomScreenProps> = ({
85115
// forceStart bypasses the "wait for first user message" gate inside the hook,
86116
// which would otherwise block ads here since no conversation exists yet.
87117
// Try Gravity first, then fall back to Carbon when Gravity doesn't fill.
88-
const { adData, recordImpression } = useGravityAd({
118+
const { ads, recordImpression } = useGravityAd({
89119
enabled: true,
90120
forceStart: true,
91121
provider: 'gravity',
@@ -221,23 +251,24 @@ export const WaitingRoomScreen: React.FC<WaitingRoomScreenProps> = ({
221251
<span fg={theme.muted}> / {session.queueDepth}</span>
222252
</text>
223253
<text style={{ fg: theme.muted, alignSelf: 'flex-start' }}>
224-
<span>Wait </span>
254+
<span>Wait </span>
225255
{session.position === 1
226256
? 'any moment now'
227257
: formatWait(session.estimatedWaitMs)}
228258
</text>
229259
<text style={{ fg: theme.muted, alignSelf: 'flex-start' }}>
230-
<span>Elapsed </span>
260+
<span>Elapsed </span>
231261
{formatElapsed(elapsedMs)}
232262
</text>
233-
{/* Per-model session quota (e.g. GLM 5.1 caps at 5/20h). Only
263+
{/* Per-model session quota (e.g. Kimi K2.6 caps at 5/12h). Only
234264
rendered for rate-limited models so the Minimax queue stays
235265
clutter-free. */}
236266
{session.rateLimit && (
237267
<text style={{ fg: theme.muted, alignSelf: 'flex-start' }}>
238268
<span>Sessions </span>
239269
<span fg={theme.foreground}>
240-
{session.rateLimit.recentCount} / {session.rateLimit.limit}
270+
{session.rateLimit.recentCount} /{' '}
271+
{session.rateLimit.limit}
241272
</span>
242273
<span> used in last {session.rateLimit.windowHours}h</span>
243274
</text>
@@ -262,10 +293,36 @@ export const WaitingRoomScreen: React.FC<WaitingRoomScreenProps> = ({
262293
⚠ Free mode isn't available in your region
263294
</text>
264295
<text style={{ fg: theme.muted, wrapMode: 'word' }}>
265-
We detected your location as{' '}
266-
<span fg={theme.foreground}>{session.countryCode}</span>,
267-
which is outside the countries where freebuff is currently
268-
offered. Press Ctrl+C to exit.
296+
{session.countryBlockReason === 'anonymous_network' ? (
297+
<>
298+
We detected{' '}
299+
{formatPrivacySignalList(session.ipPrivacySignals)} traffic
300+
{session.countryCode === 'UNKNOWN' ? (
301+
''
302+
) : (
303+
<>
304+
{' '}
305+
from{' '}
306+
<span fg={theme.foreground}>{session.countryCode}</span>
307+
</>
308+
)}
309+
. Freebuff can't be used from anonymized networks. Press
310+
Ctrl+C to exit.
311+
</>
312+
) : session.countryCode === 'UNKNOWN' ? (
313+
<>
314+
We couldn't verify an eligible location for this request.
315+
VPN, Tor, proxy, or unknown-location traffic can't use
316+
freebuff. Press Ctrl+C to exit.
317+
</>
318+
) : (
319+
<>
320+
We detected your location as{' '}
321+
<span fg={theme.foreground}>{session.countryCode}</span>,
322+
which is outside the countries where freebuff is currently
323+
offered. Press Ctrl+C to exit.
324+
</>
325+
)}
269326
</text>
270327
</>
271328
)}
@@ -279,14 +336,15 @@ export const WaitingRoomScreen: React.FC<WaitingRoomScreenProps> = ({
279336
⚠ Account unavailable
280337
</text>
281338
<text style={{ fg: theme.muted, wrapMode: 'word' }}>
282-
This account has been suspended and can't use freebuff. If you think this is a
283-
mistake, contact support@codebuff.com. Press Ctrl+C to exit.
339+
This account has been suspended and can't use freebuff. If you
340+
think this is a mistake, contact support@codebuff.com. Press
341+
Ctrl+C to exit.
284342
</text>
285343
</>
286344
)}
287345

288-
{/* Per-model session quota exhausted (e.g. 5+ GLM sessions in the
289-
last 20h). Terminal for this run — the user can exit and come
346+
{/* Per-model session quota exhausted (e.g. 5+ Kimi sessions in the
347+
last 12h). Terminal for this run — the user can exit and come
290348
back once the oldest session in the window rolls off. */}
291349
{session?.status === 'rate_limited' && (
292350
<>
@@ -311,17 +369,14 @@ export const WaitingRoomScreen: React.FC<WaitingRoomScreenProps> = ({
311369
</box>
312370

313371
{/* Ad banner pinned to the bottom, same look-and-feel as in chat. */}
314-
{adData && (
372+
{ads && (
315373
<box style={{ flexShrink: 0 }}>
316-
<ChoiceAdBanner
317-
ads={adData.variant === 'choice' ? adData.ads : [adData.ad]}
318-
onImpression={recordImpression}
319-
/>
374+
<ChoiceAdBanner ads={ads} onImpression={recordImpression} />
320375
</box>
321376
)}
322377

323378
{/* Horizontal separator (mirrors chat input divider style) */}
324-
{!adData && (
379+
{!ads && (
325380
<text style={{ fg: theme.muted, flexShrink: 0 }}>
326381
{'─'.repeat(terminalWidth)}
327382
</text>

cli/src/hooks/helpers/send-message.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { IS_FREEBUFF } from '../../utils/constants'
1212
import { processBashContext } from '../../utils/bash-context-processor'
1313
import { markRunningAgentsAsCancelled } from '../../utils/block-operations'
1414
import {
15-
getCountryCodeFromFreeModeError,
15+
getCountryBlockFromFreeModeError,
1616
getFreebuffGateErrorKind,
1717
isOutOfCreditsError,
1818
isFreeModeUnavailableError,
@@ -394,7 +394,9 @@ export const handleRunCompletion = (params: {
394394
updater.setError(FREE_MODE_UNAVAILABLE_MESSAGE)
395395
if (IS_FREEBUFF) {
396396
markFreebuffSessionCountryBlocked(
397-
getCountryCodeFromFreeModeError(output) ?? 'UNKNOWN',
397+
getCountryBlockFromFreeModeError(output) ?? {
398+
countryCode: 'UNKNOWN',
399+
},
398400
)
399401
}
400402
finalizeAfterError()
@@ -494,7 +496,9 @@ export const handleRunError = (params: {
494496
updater.setError(FREE_MODE_UNAVAILABLE_MESSAGE)
495497
if (IS_FREEBUFF) {
496498
markFreebuffSessionCountryBlocked(
497-
getCountryCodeFromFreeModeError(error) ?? 'UNKNOWN',
499+
getCountryBlockFromFreeModeError(error) ?? {
500+
countryCode: 'UNKNOWN',
501+
},
498502
)
499503
}
500504
return

0 commit comments

Comments
 (0)