diff --git a/agents/__tests__/editor.test.ts b/agents/__tests__/editor.test.ts
index dd5630930..6a78c8fd6 100644
--- a/agents/__tests__/editor.test.ts
+++ b/agents/__tests__/editor.test.ts
@@ -62,9 +62,9 @@ describe('editor agent', () => {
expect(gpt5Editor.model).toBe('openai/gpt-5.1')
})
- test('creates glm editor', () => {
- const glmEditor = createCodeEditor({ model: 'glm' })
- expect(glmEditor.model).toBe('z-ai/glm-5.1')
+ test('creates kimi editor', () => {
+ const kimiEditor = createCodeEditor({ model: 'kimi' })
+ expect(kimiEditor.model).toBe('moonshotai/kimi-k2.6')
})
test('creates kimi editor', () => {
@@ -83,10 +83,10 @@ describe('editor agent', () => {
expect(gpt5Editor.instructionsPrompt).not.toContain('')
})
- test('glm editor does not include think tags in instructions', () => {
- const glmEditor = createCodeEditor({ model: 'glm' })
- expect(glmEditor.instructionsPrompt).not.toContain('')
- expect(glmEditor.instructionsPrompt).not.toContain('')
+ test('kimi editor does not include think tags in instructions', () => {
+ const kimiEditor = createCodeEditor({ model: 'kimi' })
+ expect(kimiEditor.instructionsPrompt).not.toContain('')
+ expect(kimiEditor.instructionsPrompt).not.toContain('')
})
test('kimi editor does not include think tags in instructions', () => {
@@ -110,17 +110,17 @@ describe('editor agent', () => {
test('all variants have same base properties', () => {
const opusEditor = createCodeEditor({ model: 'opus' })
const gpt5Editor = createCodeEditor({ model: 'gpt-5' })
- const glmEditor = createCodeEditor({ model: 'glm' })
+ const kimiEditor = createCodeEditor({ model: 'kimi' })
// All should have same basic structure
expect(opusEditor.displayName).toBe(gpt5Editor.displayName)
- expect(gpt5Editor.displayName).toBe(glmEditor.displayName)
+ expect(gpt5Editor.displayName).toBe(kimiEditor.displayName)
expect(opusEditor.outputMode).toBe(gpt5Editor.outputMode)
- expect(gpt5Editor.outputMode).toBe(glmEditor.outputMode)
+ expect(gpt5Editor.outputMode).toBe(kimiEditor.outputMode)
expect(opusEditor.toolNames).toEqual(gpt5Editor.toolNames)
- expect(gpt5Editor.toolNames).toEqual(glmEditor.toolNames)
+ expect(gpt5Editor.toolNames).toEqual(kimiEditor.toolNames)
})
})
diff --git a/agents/editor/editor.ts b/agents/editor/editor.ts
index bb31eaaeb..0206d375b 100644
--- a/agents/editor/editor.ts
+++ b/agents/editor/editor.ts
@@ -1,4 +1,3 @@
-
import { publisher } from '../constants'
import type { AgentDefinition } from '../types/agent-definition'
@@ -34,7 +33,7 @@ export const createCodeEditor = (options: {
inheritParentSystemPrompt: true,
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.
-
+
Your task is to write out ALL the code changes needed to complete the user's request in a single comprehensive response.
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!
diff --git a/cli/src/utils/__tests__/freebuff-model-navigation.test.ts b/cli/src/utils/__tests__/freebuff-model-navigation.test.ts
index 4723245ba..16efef166 100644
--- a/cli/src/utils/__tests__/freebuff-model-navigation.test.ts
+++ b/cli/src/utils/__tests__/freebuff-model-navigation.test.ts
@@ -7,40 +7,40 @@ import {
describe('nextSelectableFreebuffModelId', () => {
test('skips unavailable models when moving forward', () => {
- const modelIds = ['glm', 'minimax']
+ const modelIds = ['kimi', 'minimax']
expect(
nextSelectableFreebuffModelId({
modelIds,
focusedId: 'minimax',
direction: 'forward',
- isSelectable: (id) => id !== 'glm',
+ isSelectable: (id) => id !== 'kimi',
}),
).toBe('minimax')
})
test('skips unavailable models when moving backward', () => {
- const modelIds = ['glm', 'minimax']
+ const modelIds = ['kimi', 'minimax']
expect(
nextSelectableFreebuffModelId({
modelIds,
focusedId: 'minimax',
direction: 'backward',
- isSelectable: (id) => id !== 'glm',
+ isSelectable: (id) => id !== 'kimi',
}),
).toBe('minimax')
})
test('moves to the next available model when more than one is selectable', () => {
- const modelIds = ['glm', 'minimax', 'other']
+ const modelIds = ['kimi', 'minimax', 'other']
expect(
nextSelectableFreebuffModelId({
modelIds,
focusedId: 'minimax',
direction: 'forward',
- isSelectable: (id) => id !== 'glm',
+ isSelectable: (id) => id !== 'kimi',
}),
).toBe('other')
})
@@ -48,8 +48,8 @@ describe('nextSelectableFreebuffModelId', () => {
test('returns null when no selectable model exists', () => {
expect(
nextSelectableFreebuffModelId({
- modelIds: ['glm'],
- focusedId: 'glm',
+ modelIds: ['kimi'],
+ focusedId: 'kimi',
direction: 'forward',
isSelectable: () => false,
}),
@@ -61,10 +61,10 @@ describe('resolveFreebuffModelCommitTarget', () => {
test('falls back to the selected model when focus is on a closed model', () => {
expect(
resolveFreebuffModelCommitTarget({
- focusedId: 'glm',
+ focusedId: 'kimi',
selectedId: 'minimax',
committedId: null,
- isSelectable: (id) => id !== 'glm',
+ isSelectable: (id) => id !== 'kimi',
}),
).toBe('minimax')
})
@@ -73,7 +73,7 @@ describe('resolveFreebuffModelCommitTarget', () => {
expect(
resolveFreebuffModelCommitTarget({
focusedId: 'minimax',
- selectedId: 'glm',
+ selectedId: 'kimi',
committedId: null,
isSelectable: (id) => id === 'minimax',
}),
diff --git a/common/src/constants/freebuff-models.ts b/common/src/constants/freebuff-models.ts
index 9c6ff423e..ca3ccb5ad 100644
--- a/common/src/constants/freebuff-models.ts
+++ b/common/src/constants/freebuff-models.ts
@@ -39,7 +39,7 @@ interface LocalTimeFormatOptions {
timeZone?: string
}
-export const FREEBUFF_MODELS = [
+export const FREEBUFF_MODELS: readonly FreebuffModelOption[] = [
{
id: FREEBUFF_MINIMAX_MODEL_ID,
displayName: 'MiniMax M2.7',
@@ -50,16 +50,15 @@ export const FREEBUFF_MODELS = [
id: FREEBUFF_KIMI_MODEL_ID,
displayName: 'Kimi K2.6',
tagline: 'Smartest',
- availability: 'deployment_hours',
+ availability: 'always',
},
-] as const satisfies readonly FreebuffModelOption[]
+]
-export type FreebuffModelId = (typeof FREEBUFF_MODELS)[number]['id']
+export type FreebuffModelId =
+ | typeof FREEBUFF_MINIMAX_MODEL_ID
+ | typeof FREEBUFF_KIMI_MODEL_ID
-/** What new freebuff users see selected in the picker. May not be currently
- * available (Kimi is closed outside deployment hours); callers that need an
- * always-available id for resolution / auto-fallbacks should use
- * FALLBACK_FREEBUFF_MODEL_ID instead. */
+/** What new freebuff users see selected in the picker. */
export const DEFAULT_FREEBUFF_MODEL_ID: FreebuffModelId = FREEBUFF_KIMI_MODEL_ID
/** Always-available fallback used when the requested model can't be served
diff --git a/web/src/app/api/v1/chat/completions/__tests__/completions.test.ts b/web/src/app/api/v1/chat/completions/__tests__/completions.test.ts
index ce28f91e0..660839ea2 100644
--- a/web/src/app/api/v1/chat/completions/__tests__/completions.test.ts
+++ b/web/src/app/api/v1/chat/completions/__tests__/completions.test.ts
@@ -1,7 +1,6 @@
import { afterEach, beforeEach, describe, expect, mock, it } from 'bun:test'
import { NextRequest } from 'next/server'
-import { isFreebuffDeploymentHours } from '@codebuff/common/constants/freebuff-models'
import { formatQuotaResetCountdown, postChatCompletions } from '../_post'
import type { TrackEventFn } from '@codebuff/common/types/contracts/analytics'
@@ -642,15 +641,15 @@ describe('/api/v1/chat/completions POST endpoint', () => {
expect(body.countryBlockReason).toBe('anonymized_or_unknown_country')
})
- it('lets freebuff use Kimi K2.6 through Fireworks availability rules', async () => {
+ it('lets freebuff use Kimi K2.6 through CanopyWave', async () => {
const fetchedBodies: Record[] = []
- const fetchViaFireworks = mock(
+ const fetchViaCanopyWave = mock(
async (_url: string | URL | Request, init?: RequestInit) => {
fetchedBodies.push(JSON.parse(init?.body as string))
return new Response(
JSON.stringify({
id: 'test-id',
- model: 'accounts/fireworks/models/kimi-k2p6',
+ model: 'moonshotai/kimi-k2.6',
choices: [{ message: { content: 'test response' } }],
usage: {
prompt_tokens: 10,
@@ -690,26 +689,18 @@ describe('/api/v1/chat/completions POST endpoint', () => {
trackEvent: mockTrackEvent,
getUserUsageData: mockGetUserUsageData,
getAgentRunFromId: mockGetAgentRunFromId,
- fetch: fetchViaFireworks,
+ fetch: fetchViaCanopyWave,
insertMessageBigquery: mockInsertMessageBigquery,
loggerWithContext: mockLoggerWithContext,
checkSessionAdmissible: mockCheckSessionAdmissibleAllow,
})
const body = await response.json()
- if (isFreebuffDeploymentHours()) {
- expect(response.status).toBe(200)
- expect(fetchedBodies).toHaveLength(1)
- expect(fetchedBodies[0].model).toBe(
- 'accounts/fireworks/models/kimi-k2p6',
- )
- expect(body.model).toBe('moonshotai/kimi-k2.6')
- expect(body.provider).toBe('Fireworks')
- } else {
- expect(response.status).toBe(503)
- expect(fetchedBodies).toHaveLength(0)
- expect(body.error.code).toBe('DEPLOYMENT_OUTSIDE_HOURS')
- }
+ expect(response.status).toBe(200)
+ expect(fetchedBodies).toHaveLength(1)
+ expect(fetchedBodies[0].model).toBe('moonshotai/kimi-k2.6')
+ expect(body.model).toBe('moonshotai/kimi-k2.6')
+ expect(body.provider).toBe('CanopyWave')
})
it('skips credit check when in FREE mode even with 0 credits', async () => {
diff --git a/web/src/app/api/v1/freebuff/session/__tests__/session.test.ts b/web/src/app/api/v1/freebuff/session/__tests__/session.test.ts
index 54481dca8..e7eaab8b5 100644
--- a/web/src/app/api/v1/freebuff/session/__tests__/session.test.ts
+++ b/web/src/app/api/v1/freebuff/session/__tests__/session.test.ts
@@ -281,19 +281,6 @@ describe('POST /api/v1/freebuff/session', () => {
expect(body.status).toBe('queued')
})
- test('returns model_unavailable for Kimi outside deployment hours', async () => {
- const sessionDeps = makeSessionDeps()
- const resp = await postFreebuffSession(
- makeReq('ok', { model: 'moonshotai/kimi-k2.6' }),
- makeDeps(sessionDeps, 'u1'),
- )
- expect(resp.status).toBe(409)
- const body = await resp.json()
- expect(body.status).toBe('model_unavailable')
- expect(body.availableHours).toBe('9am ET-5pm PT every day')
- expect(sessionDeps.rows.size).toBe(0)
- })
-
// Banned bots with valid API keys were POSTing every few seconds and
// inflating queueDepth between the 15s admission-tick sweeps. Rejecting at
// the HTTP layer with 403 (terminal, like country_blocked) keeps them out
diff --git a/web/src/server/free-session/__tests__/public-api.test.ts b/web/src/server/free-session/__tests__/public-api.test.ts
index 7f08d2bdd..f92b7953c 100644
--- a/web/src/server/free-session/__tests__/public-api.test.ts
+++ b/web/src/server/free-session/__tests__/public-api.test.ts
@@ -200,20 +200,6 @@ describe('requestSession', () => {
expect(state.instanceId).toBe('inst-1')
})
- test('deployment-hours-only model is unavailable outside deployment hours', async () => {
- const state = await requestSession({
- userId: 'u1',
- model: 'moonshotai/kimi-k2.6',
- deps,
- })
- expect(state).toEqual({
- status: 'model_unavailable',
- requestedModel: 'moonshotai/kimi-k2.6',
- availableHours: '9am ET-5pm PT every day',
- })
- expect(deps.rows.size).toBe(0)
- })
-
test('queued response includes a per-model depth snapshot for the selector', async () => {
deps._tick(new Date('2026-04-17T16:00:00Z'))
// Seed 2 users in MiniMax + 1 in Kimi so the returned map captures both.
@@ -325,9 +311,7 @@ describe('requestSession', () => {
// Per-user rate limit (5 Kimi admissions per 12h) — the wire limit is
// hard-coded in public-api.ts, so tests seed the fake admit log directly
- // rather than configuring it. Kimi also has deployment-hours gating, so
- // these tests bump `now` into the open window (12pm ET on a weekday)
- // before issuing the request.
+ // rather than configuring it.
const KIMI_MODEL = 'moonshotai/kimi-k2.6'
const KIMI_LIMIT = 5
const KIMI_WINDOW_HOURS = 12
@@ -636,8 +620,8 @@ describe('getSessionState', () => {
// Regression: the POST response attached rateLimit, but GET polls did
// not — so the "Sessions N/M used" line flashed once then disappeared on
// the next 5s poll. GET must attach the same quota snapshot. Rate
- // limits only apply to Kimi, so this test uses Kimi explicitly (inside
- // deployment hours) rather than the Minimax DEFAULT_MODEL.
+ // limits only apply to Kimi, so this test uses Kimi explicitly rather
+ // than the Minimax DEFAULT_MODEL.
deps._tick(new Date('2026-04-17T16:00:00Z'))
const now = deps._now()
deps.admits.push({