Skip to content

Commit ba5871f

Browse files
committed
fix(refactor): address Wave 1 review findings
- Update 6 deprecated imports from old-constants to new domain paths: - sdk/src/run.ts, env.ts, client.ts -> constants/paths - packages/billing/src/auto-topup.ts -> constants/limits - packages/agent-runtime/src/process-file-block.ts -> constants/model-config - common/src/project-file-tree.ts -> constants/paths - Remove unused FileTreeError and PermissionError classes from project-file-tree.ts - Simplify logFileTreeError to only log in debug mode (remove confusing ENOENT/EACCES special case) - Export helper functions from context-pruner.ts for testability: - truncateLongText, estimateTokens, getTextContent, summarizeToolCall
1 parent b70e947 commit ba5871f

File tree

14 files changed

+1765
-617
lines changed

14 files changed

+1765
-617
lines changed

REFACTORING_PLAN.md

Lines changed: 1078 additions & 0 deletions
Large diffs are not rendered by default.

agents/context-pruner.ts

Lines changed: 286 additions & 251 deletions
Large diffs are not rendered by default.

common/src/constants/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Re-export all constants from domain-specific files for backwards compatibility
2+
// This allows existing imports from '@codebuff/common/old-constants' to continue working
3+
4+
export * from './model-config'
5+
export * from './limits'
6+
export * from './ui'
7+
export * from './paths'

common/src/constants/limits.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export const PROFIT_MARGIN = 0.055
2+
3+
export const REQUEST_CREDIT_SHOW_THRESHOLD = 1
4+
export const MAX_DATE = new Date(86399999999999)
5+
export const BILLING_PERIOD_DAYS = 30
6+
export const SESSION_MAX_AGE_SECONDS = 30 * 24 * 60 * 60 // 30 days
7+
export const SESSION_TIME_WINDOW_MS = 30 * 60 * 1000 // 30 minutes - used for matching sessions created around fingerprint creation
8+
export const CREDITS_REFERRAL_BONUS = 250
9+
export const AFFILIATE_USER_REFFERAL_LIMIT = 500
10+
11+
// Default number of free credits granted per cycle
12+
export const DEFAULT_FREE_CREDITS_GRANT = 500
13+
14+
// Credit pricing configuration
15+
export const CREDIT_PRICING = {
16+
CENTS_PER_CREDIT: 1, // 1 credit = 1 cent = $0.01
17+
MIN_PURCHASE_CREDITS: 100, // $1.00 minimum
18+
DISPLAY_RATE: '$0.01 per credit',
19+
} as const
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
import { isExplicitlyDefinedModel } from '../util/model-utils'
2+
3+
// Allowed model prefixes for validation
4+
export const ALLOWED_MODEL_PREFIXES = [
5+
'anthropic',
6+
'openai',
7+
'google',
8+
'x-ai',
9+
] as const
10+
11+
export const costModes = [
12+
'lite',
13+
'normal',
14+
'max',
15+
'experimental',
16+
'ask',
17+
] as const
18+
export type CostMode = (typeof costModes)[number]
19+
20+
export const openaiModels = {
21+
gpt4_1: 'gpt-4.1-2025-04-14',
22+
gpt4o: 'gpt-4o-2024-11-20',
23+
gpt4omini: 'gpt-4o-mini-2024-07-18',
24+
o3mini: 'o3-mini-2025-01-31',
25+
o3: 'o3-2025-04-16',
26+
o3pro: 'o3-pro-2025-06-10',
27+
o4mini: 'o4-mini-2025-04-16',
28+
generatePatch:
29+
'ft:gpt-4o-2024-08-06:manifold-markets:generate-patch-batch2:AKYtDIhk',
30+
} as const
31+
export type OpenAIModel = (typeof openaiModels)[keyof typeof openaiModels]
32+
33+
export const openrouterModels = {
34+
openrouter_claude_sonnet_4_5: 'anthropic/claude-sonnet-4.5',
35+
openrouter_claude_sonnet_4: 'anthropic/claude-4-sonnet-20250522',
36+
openrouter_claude_opus_4: 'anthropic/claude-opus-4.1',
37+
openrouter_claude_3_5_haiku: 'anthropic/claude-3.5-haiku-20241022',
38+
openrouter_claude_3_5_sonnet: 'anthropic/claude-3.5-sonnet-20240620',
39+
openrouter_gpt4o: 'openai/gpt-4o-2024-11-20',
40+
openrouter_gpt5: 'openai/gpt-5.1',
41+
openrouter_gpt5_chat: 'openai/gpt-5.1-chat',
42+
openrouter_gpt4o_mini: 'openai/gpt-4o-mini-2024-07-18',
43+
openrouter_gpt4_1_nano: 'openai/gpt-4.1-nano',
44+
openrouter_o3_mini: 'openai/o3-mini-2025-01-31',
45+
openrouter_gemini2_5_pro_preview: 'google/gemini-2.5-pro',
46+
openrouter_gemini2_5_flash: 'google/gemini-2.5-flash',
47+
openrouter_gemini2_5_flash_thinking:
48+
'google/gemini-2.5-flash-preview:thinking',
49+
openrouter_grok_4: 'x-ai/grok-4-07-09',
50+
} as const
51+
export type openrouterModel =
52+
(typeof openrouterModels)[keyof typeof openrouterModels]
53+
54+
export const deepseekModels = {
55+
deepseekChat: 'deepseek-chat',
56+
deepseekReasoner: 'deepseek-reasoner',
57+
} as const
58+
export type DeepseekModel = (typeof deepseekModels)[keyof typeof deepseekModels]
59+
60+
// Vertex uses "endpoint IDs" for finetuned models, which are just integers
61+
export const finetunedVertexModels = {
62+
ft_filepicker_003: '196166068534771712',
63+
ft_filepicker_005: '8493203957034778624',
64+
ft_filepicker_007: '2589952415784501248',
65+
ft_filepicker_topk_001: '3676445825887633408',
66+
ft_filepicker_008: '2672143108984012800',
67+
ft_filepicker_topk_002: '1694861989844615168',
68+
ft_filepicker_010: '3808739064941641728',
69+
ft_filepicker_010_epoch_2: '6231675664466968576',
70+
ft_filepicker_topk_003: '1502192368286171136',
71+
} as const
72+
export const finetunedVertexModelNames: Record<string, string> = {
73+
[finetunedVertexModels.ft_filepicker_003]: 'ft_filepicker_003',
74+
[finetunedVertexModels.ft_filepicker_005]: 'ft_filepicker_005',
75+
[finetunedVertexModels.ft_filepicker_007]: 'ft_filepicker_007',
76+
[finetunedVertexModels.ft_filepicker_topk_001]: 'ft_filepicker_topk_001',
77+
[finetunedVertexModels.ft_filepicker_008]: 'ft_filepicker_008',
78+
[finetunedVertexModels.ft_filepicker_topk_002]: 'ft_filepicker_topk_002',
79+
[finetunedVertexModels.ft_filepicker_010]: 'ft_filepicker_010',
80+
[finetunedVertexModels.ft_filepicker_010_epoch_2]:
81+
'ft_filepicker_010_epoch_2',
82+
[finetunedVertexModels.ft_filepicker_topk_003]: 'ft_filepicker_topk_003',
83+
}
84+
export type FinetunedVertexModel =
85+
(typeof finetunedVertexModels)[keyof typeof finetunedVertexModels]
86+
87+
export const models = {
88+
...openaiModels,
89+
...deepseekModels,
90+
...openrouterModels,
91+
...finetunedVertexModels,
92+
} as const
93+
94+
export const shortModelNames = {
95+
'gemini-2.5-pro': models.openrouter_gemini2_5_pro_preview,
96+
'flash-2.5': models.openrouter_gemini2_5_flash,
97+
'opus-4': models.openrouter_claude_opus_4,
98+
'sonnet-4.5': models.openrouter_claude_sonnet_4_5,
99+
'sonnet-4': models.openrouter_claude_sonnet_4,
100+
'sonnet-3.7': models.openrouter_claude_sonnet_4,
101+
'sonnet-3.6': models.openrouter_claude_3_5_sonnet,
102+
'sonnet-3.5': models.openrouter_claude_3_5_sonnet,
103+
'gpt-4.1': models.gpt4_1,
104+
'o3-mini': models.o3mini,
105+
o3: models.o3,
106+
'o4-mini': models.o4mini,
107+
'o3-pro': models.o3pro,
108+
}
109+
110+
export const providerModelNames = {
111+
...Object.fromEntries(
112+
Object.entries(openaiModels).map(([name, model]) => [
113+
model,
114+
'openai' as const,
115+
]),
116+
),
117+
...Object.fromEntries(
118+
Object.entries(openrouterModels).map(([name, model]) => [
119+
model,
120+
'openrouter' as const,
121+
]),
122+
),
123+
}
124+
125+
export type Model = (typeof models)[keyof typeof models] | (string & {})
126+
127+
export const shouldCacheModels = [
128+
'anthropic/claude-opus-4.1',
129+
'anthropic/claude-sonnet-4',
130+
'anthropic/claude-opus-4',
131+
'anthropic/claude-3.7-sonnet',
132+
'anthropic/claude-3.5-haiku',
133+
'z-ai/glm-4.5',
134+
'qwen/qwen3-coder',
135+
]
136+
const nonCacheableModels = [
137+
models.openrouter_grok_4,
138+
] satisfies string[] as string[]
139+
export function supportsCacheControl(model: Model): boolean {
140+
if (model.startsWith('openai/')) {
141+
return true
142+
}
143+
if (model.startsWith('anthropic/')) {
144+
return true
145+
}
146+
if (!isExplicitlyDefinedModel(model)) {
147+
// Default to no cache control for unknown models
148+
return false
149+
}
150+
return !nonCacheableModels.includes(model)
151+
}
152+
153+
export function getModelFromShortName(
154+
modelName: string | undefined,
155+
): Model | undefined {
156+
if (!modelName) return undefined
157+
if (modelName && !(modelName in shortModelNames)) {
158+
throw new Error(
159+
`Unknown model: ${modelName}. Please use a valid model. Valid models are: ${Object.keys(
160+
shortModelNames,
161+
).join(', ')}`,
162+
)
163+
}
164+
165+
return shortModelNames[modelName as keyof typeof shortModelNames]
166+
}
167+
168+
export const providerDomains = {
169+
google: 'google.com',
170+
anthropic: 'anthropic.com',
171+
openai: 'chatgpt.com',
172+
deepseek: 'deepseek.com',
173+
xai: 'x.ai',
174+
} as const
175+
176+
export function getLogoForModel(modelName: string): string | undefined {
177+
let domain: string | undefined
178+
179+
if (Object.values(openaiModels).includes(modelName as OpenAIModel))
180+
domain = providerDomains.openai
181+
else if (Object.values(deepseekModels).includes(modelName as DeepseekModel))
182+
domain = providerDomains.deepseek
183+
else if (modelName.includes('claude')) domain = providerDomains.anthropic
184+
else if (modelName.includes('grok')) domain = providerDomains.xai
185+
186+
return domain
187+
? `https://www.google.com/s2/favicons?domain=${domain}&sz=256`
188+
: undefined
189+
}
190+
191+
export const getModelForMode = (
192+
costMode: CostMode,
193+
operation: 'agent' | 'file-requests' | 'check-new-files',
194+
) => {
195+
if (operation === 'agent') {
196+
return {
197+
lite: models.openrouter_gemini2_5_flash,
198+
normal: models.openrouter_claude_sonnet_4,
199+
max: models.openrouter_claude_sonnet_4,
200+
experimental: models.openrouter_gemini2_5_pro_preview,
201+
ask: models.openrouter_gemini2_5_pro_preview,
202+
}[costMode]
203+
}
204+
if (operation === 'file-requests') {
205+
return {
206+
lite: models.openrouter_claude_3_5_haiku,
207+
normal: models.openrouter_claude_3_5_haiku,
208+
max: models.openrouter_claude_sonnet_4,
209+
experimental: models.openrouter_claude_sonnet_4,
210+
ask: models.openrouter_claude_3_5_haiku,
211+
}[costMode]
212+
}
213+
if (operation === 'check-new-files') {
214+
return {
215+
lite: models.openrouter_claude_3_5_haiku,
216+
normal: models.openrouter_claude_sonnet_4,
217+
max: models.openrouter_claude_sonnet_4,
218+
experimental: models.openrouter_claude_sonnet_4,
219+
ask: models.openrouter_claude_sonnet_4,
220+
}[costMode]
221+
}
222+
throw new Error(`Unknown operation: ${operation}`)
223+
}

common/src/constants/paths.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
export const STOP_MARKER = '[' + 'END]'
2+
export const FIND_FILES_MARKER = '[' + 'FIND_FILES_PLEASE]'
3+
export const EXISTING_CODE_MARKER = '[[**REPLACE_WITH_EXISTING_CODE**]]'
4+
5+
// Directory where agent template override files are stored
6+
export const AGENT_TEMPLATES_DIR = '.agents/'
7+
export const AGENT_DEFINITION_FILE = 'agent-definition.d.ts'
8+
9+
export const API_KEY_ENV_VAR = 'CODEBUFF_API_KEY'
10+
11+
export const INVALID_AUTH_TOKEN_MESSAGE =
12+
'Invalid auth token. You may have been logged out from the web portal. Please log in again.'
13+
14+
export const DEFAULT_IGNORED_PATHS = [
15+
'.git',
16+
'.env',
17+
'.env.*',
18+
'*.min.*',
19+
'node_modules',
20+
'venv',
21+
'virtualenv',
22+
'.venv',
23+
'.virtualenv',
24+
'__pycache__',
25+
'*.egg-info/',
26+
'*.pyc',
27+
'.DS_Store',
28+
'.pytest_cache',
29+
'.mypy_cache',
30+
'.ruff_cache',
31+
'.next',
32+
'package-lock.json',
33+
'bun.lockb',
34+
]
35+
36+
// Special message content tags indicating specific server states
37+
export const ASKED_CONFIG = 'asked_config'
38+
export const SHOULD_ASK_CONFIG = 'should_ask_config'
39+
export const ONE_TIME_TAGS = [] as const
40+
export const ONE_TIME_LABELS = [
41+
...ONE_TIME_TAGS,
42+
ASKED_CONFIG,
43+
SHOULD_ASK_CONFIG,
44+
] as const
45+
46+
export const FILE_READ_STATUS = {
47+
DOES_NOT_EXIST: '[FILE_DOES_NOT_EXIST]',
48+
IGNORED: '[BLOCKED]',
49+
TEMPLATE: '[TEMPLATE]',
50+
OUTSIDE_PROJECT: '[FILE_OUTSIDE_PROJECT]',
51+
TOO_LARGE: '[FILE_TOO_LARGE]',
52+
ERROR: '[FILE_READ_ERROR]',
53+
} as const
54+
55+
export const HIDDEN_FILE_READ_STATUS = [
56+
FILE_READ_STATUS.DOES_NOT_EXIST,
57+
FILE_READ_STATUS.IGNORED,
58+
FILE_READ_STATUS.OUTSIDE_PROJECT,
59+
FILE_READ_STATUS.TOO_LARGE,
60+
FILE_READ_STATUS.ERROR,
61+
]
62+
63+
export function toOptionalFile(file: string | null) {
64+
if (file === null) return null
65+
return HIDDEN_FILE_READ_STATUS.some((status) => file.startsWith(status))
66+
? null
67+
: file
68+
}
69+
70+
export const TEST_USER_ID = 'test-user-id'

common/src/constants/ui.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export const AuthState = {
2+
LOGGED_OUT: 'LOGGED_OUT',
3+
LOGGED_IN: 'LOGGED_IN',
4+
} as const
5+
6+
export type AuthState = (typeof AuthState)[keyof typeof AuthState]
7+
8+
export const UserState = {
9+
LOGGED_OUT: 'LOGGED_OUT',
10+
GOOD_STANDING: 'GOOD_STANDING', // >= 100 credits
11+
ATTENTION_NEEDED: 'ATTENTION_NEEDED', // 20-99 credits
12+
CRITICAL: 'CRITICAL', // 1-19 credits
13+
DEPLETED: 'DEPLETED', // <= 0 credits
14+
} as const
15+
16+
export type UserState = (typeof UserState)[keyof typeof UserState]
17+
18+
export function getUserState(isLoggedIn: boolean, credits: number): UserState {
19+
if (!isLoggedIn) return UserState.LOGGED_OUT
20+
21+
if (credits >= 100) return UserState.GOOD_STANDING
22+
if (credits >= 20) return UserState.ATTENTION_NEEDED
23+
if (credits >= 1) return UserState.CRITICAL
24+
return UserState.DEPLETED
25+
}

0 commit comments

Comments
 (0)