@shareai-lab/kode 1.1.14 → 1.1.16-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/cli.js +77 -82
  2. package/dist/entrypoints/cli.js +59 -38
  3. package/dist/entrypoints/cli.js.map +3 -3
  4. package/dist/index.js +5 -26
  5. package/dist/package.json +4 -1
  6. package/package.json +11 -104
  7. package/dist/test/testAdapters.js +0 -88
  8. package/dist/test/testAdapters.js.map +0 -1
  9. package/src/ProjectOnboarding.tsx +0 -198
  10. package/src/Tool.ts +0 -83
  11. package/src/commands/agents.tsx +0 -3416
  12. package/src/commands/approvedTools.ts +0 -53
  13. package/src/commands/bug.tsx +0 -20
  14. package/src/commands/clear.ts +0 -43
  15. package/src/commands/compact.ts +0 -120
  16. package/src/commands/config.tsx +0 -19
  17. package/src/commands/cost.ts +0 -18
  18. package/src/commands/ctx_viz.ts +0 -209
  19. package/src/commands/doctor.ts +0 -24
  20. package/src/commands/help.tsx +0 -19
  21. package/src/commands/init.ts +0 -37
  22. package/src/commands/listen.ts +0 -42
  23. package/src/commands/login.tsx +0 -51
  24. package/src/commands/logout.tsx +0 -40
  25. package/src/commands/mcp.ts +0 -41
  26. package/src/commands/model.tsx +0 -40
  27. package/src/commands/modelstatus.tsx +0 -20
  28. package/src/commands/onboarding.tsx +0 -34
  29. package/src/commands/pr_comments.ts +0 -59
  30. package/src/commands/refreshCommands.ts +0 -54
  31. package/src/commands/release-notes.ts +0 -34
  32. package/src/commands/resume.tsx +0 -31
  33. package/src/commands/review.ts +0 -49
  34. package/src/commands/terminalSetup.ts +0 -221
  35. package/src/commands.ts +0 -139
  36. package/src/components/ApproveApiKey.tsx +0 -93
  37. package/src/components/AsciiLogo.tsx +0 -13
  38. package/src/components/AutoUpdater.tsx +0 -148
  39. package/src/components/Bug.tsx +0 -367
  40. package/src/components/Config.tsx +0 -293
  41. package/src/components/ConsoleOAuthFlow.tsx +0 -327
  42. package/src/components/Cost.tsx +0 -23
  43. package/src/components/CostThresholdDialog.tsx +0 -46
  44. package/src/components/CustomSelect/option-map.ts +0 -42
  45. package/src/components/CustomSelect/select-option.tsx +0 -78
  46. package/src/components/CustomSelect/select.tsx +0 -152
  47. package/src/components/CustomSelect/theme.ts +0 -45
  48. package/src/components/CustomSelect/use-select-state.ts +0 -414
  49. package/src/components/CustomSelect/use-select.ts +0 -35
  50. package/src/components/FallbackToolUseRejectedMessage.tsx +0 -15
  51. package/src/components/FileEditToolUpdatedMessage.tsx +0 -66
  52. package/src/components/Help.tsx +0 -215
  53. package/src/components/HighlightedCode.tsx +0 -33
  54. package/src/components/InvalidConfigDialog.tsx +0 -113
  55. package/src/components/Link.tsx +0 -32
  56. package/src/components/LogSelector.tsx +0 -86
  57. package/src/components/Logo.tsx +0 -170
  58. package/src/components/MCPServerApprovalDialog.tsx +0 -100
  59. package/src/components/MCPServerDialogCopy.tsx +0 -25
  60. package/src/components/MCPServerMultiselectDialog.tsx +0 -109
  61. package/src/components/Message.tsx +0 -221
  62. package/src/components/MessageResponse.tsx +0 -15
  63. package/src/components/MessageSelector.tsx +0 -211
  64. package/src/components/ModeIndicator.tsx +0 -88
  65. package/src/components/ModelConfig.tsx +0 -301
  66. package/src/components/ModelListManager.tsx +0 -227
  67. package/src/components/ModelSelector.tsx +0 -3387
  68. package/src/components/ModelStatusDisplay.tsx +0 -230
  69. package/src/components/Onboarding.tsx +0 -274
  70. package/src/components/PressEnterToContinue.tsx +0 -11
  71. package/src/components/PromptInput.tsx +0 -760
  72. package/src/components/SentryErrorBoundary.ts +0 -39
  73. package/src/components/Spinner.tsx +0 -129
  74. package/src/components/StickerRequestForm.tsx +0 -16
  75. package/src/components/StructuredDiff.tsx +0 -191
  76. package/src/components/TextInput.tsx +0 -259
  77. package/src/components/TodoItem.tsx +0 -47
  78. package/src/components/TokenWarning.tsx +0 -31
  79. package/src/components/ToolUseLoader.tsx +0 -40
  80. package/src/components/TrustDialog.tsx +0 -106
  81. package/src/components/binary-feedback/BinaryFeedback.tsx +0 -63
  82. package/src/components/binary-feedback/BinaryFeedbackOption.tsx +0 -111
  83. package/src/components/binary-feedback/BinaryFeedbackView.tsx +0 -172
  84. package/src/components/binary-feedback/utils.ts +0 -220
  85. package/src/components/messages/AssistantBashOutputMessage.tsx +0 -22
  86. package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +0 -49
  87. package/src/components/messages/AssistantRedactedThinkingMessage.tsx +0 -19
  88. package/src/components/messages/AssistantTextMessage.tsx +0 -144
  89. package/src/components/messages/AssistantThinkingMessage.tsx +0 -40
  90. package/src/components/messages/AssistantToolUseMessage.tsx +0 -132
  91. package/src/components/messages/TaskProgressMessage.tsx +0 -32
  92. package/src/components/messages/TaskToolMessage.tsx +0 -58
  93. package/src/components/messages/UserBashInputMessage.tsx +0 -28
  94. package/src/components/messages/UserCommandMessage.tsx +0 -30
  95. package/src/components/messages/UserKodingInputMessage.tsx +0 -28
  96. package/src/components/messages/UserPromptMessage.tsx +0 -35
  97. package/src/components/messages/UserTextMessage.tsx +0 -39
  98. package/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx +0 -12
  99. package/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +0 -36
  100. package/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +0 -31
  101. package/src/components/messages/UserToolResultMessage/UserToolResultMessage.tsx +0 -57
  102. package/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +0 -35
  103. package/src/components/messages/UserToolResultMessage/utils.tsx +0 -56
  104. package/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +0 -121
  105. package/src/components/permissions/FallbackPermissionRequest.tsx +0 -153
  106. package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +0 -182
  107. package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +0 -77
  108. package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +0 -164
  109. package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +0 -83
  110. package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +0 -240
  111. package/src/components/permissions/PermissionRequest.tsx +0 -101
  112. package/src/components/permissions/PermissionRequestTitle.tsx +0 -69
  113. package/src/components/permissions/hooks.ts +0 -44
  114. package/src/components/permissions/toolUseOptions.ts +0 -59
  115. package/src/components/permissions/utils.ts +0 -23
  116. package/src/constants/betas.ts +0 -5
  117. package/src/constants/claude-asterisk-ascii-art.tsx +0 -238
  118. package/src/constants/figures.ts +0 -4
  119. package/src/constants/keys.ts +0 -3
  120. package/src/constants/macros.ts +0 -11
  121. package/src/constants/modelCapabilities.ts +0 -179
  122. package/src/constants/models.ts +0 -1025
  123. package/src/constants/oauth.ts +0 -18
  124. package/src/constants/product.ts +0 -17
  125. package/src/constants/prompts.ts +0 -168
  126. package/src/constants/releaseNotes.ts +0 -7
  127. package/src/context/PermissionContext.tsx +0 -149
  128. package/src/context.ts +0 -278
  129. package/src/cost-tracker.ts +0 -84
  130. package/src/entrypoints/cli.tsx +0 -1561
  131. package/src/entrypoints/mcp.ts +0 -175
  132. package/src/history.ts +0 -25
  133. package/src/hooks/useApiKeyVerification.ts +0 -59
  134. package/src/hooks/useArrowKeyHistory.ts +0 -55
  135. package/src/hooks/useCanUseTool.ts +0 -138
  136. package/src/hooks/useCancelRequest.ts +0 -39
  137. package/src/hooks/useDoublePress.ts +0 -41
  138. package/src/hooks/useExitOnCtrlCD.ts +0 -31
  139. package/src/hooks/useInterval.ts +0 -25
  140. package/src/hooks/useLogMessages.ts +0 -16
  141. package/src/hooks/useLogStartupTime.ts +0 -12
  142. package/src/hooks/useNotifyAfterTimeout.ts +0 -65
  143. package/src/hooks/usePermissionRequestLogging.ts +0 -44
  144. package/src/hooks/useTerminalSize.ts +0 -49
  145. package/src/hooks/useTextInput.ts +0 -317
  146. package/src/hooks/useUnifiedCompletion.ts +0 -1405
  147. package/src/index.ts +0 -34
  148. package/src/messages.ts +0 -38
  149. package/src/permissions.ts +0 -268
  150. package/src/query.ts +0 -720
  151. package/src/screens/ConfigureNpmPrefix.tsx +0 -197
  152. package/src/screens/Doctor.tsx +0 -219
  153. package/src/screens/LogList.tsx +0 -68
  154. package/src/screens/REPL.tsx +0 -813
  155. package/src/screens/ResumeConversation.tsx +0 -68
  156. package/src/services/adapters/base.ts +0 -38
  157. package/src/services/adapters/chatCompletions.ts +0 -90
  158. package/src/services/adapters/responsesAPI.ts +0 -170
  159. package/src/services/browserMocks.ts +0 -66
  160. package/src/services/claude.ts +0 -2197
  161. package/src/services/customCommands.ts +0 -704
  162. package/src/services/fileFreshness.ts +0 -377
  163. package/src/services/gpt5ConnectionTest.ts +0 -340
  164. package/src/services/mcpClient.ts +0 -564
  165. package/src/services/mcpServerApproval.tsx +0 -50
  166. package/src/services/mentionProcessor.ts +0 -273
  167. package/src/services/modelAdapterFactory.ts +0 -69
  168. package/src/services/notifier.ts +0 -40
  169. package/src/services/oauth.ts +0 -357
  170. package/src/services/openai.ts +0 -1359
  171. package/src/services/responseStateManager.ts +0 -90
  172. package/src/services/sentry.ts +0 -3
  173. package/src/services/statsig.ts +0 -172
  174. package/src/services/statsigStorage.ts +0 -86
  175. package/src/services/systemReminder.ts +0 -507
  176. package/src/services/vcr.ts +0 -161
  177. package/src/test/testAdapters.ts +0 -96
  178. package/src/tools/ArchitectTool/ArchitectTool.tsx +0 -135
  179. package/src/tools/ArchitectTool/prompt.ts +0 -15
  180. package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +0 -576
  181. package/src/tools/BashTool/BashTool.tsx +0 -243
  182. package/src/tools/BashTool/BashToolResultMessage.tsx +0 -38
  183. package/src/tools/BashTool/OutputLine.tsx +0 -49
  184. package/src/tools/BashTool/prompt.ts +0 -174
  185. package/src/tools/BashTool/utils.ts +0 -56
  186. package/src/tools/FileEditTool/FileEditTool.tsx +0 -319
  187. package/src/tools/FileEditTool/prompt.ts +0 -51
  188. package/src/tools/FileEditTool/utils.ts +0 -58
  189. package/src/tools/FileReadTool/FileReadTool.tsx +0 -404
  190. package/src/tools/FileReadTool/prompt.ts +0 -7
  191. package/src/tools/FileWriteTool/FileWriteTool.tsx +0 -301
  192. package/src/tools/FileWriteTool/prompt.ts +0 -10
  193. package/src/tools/GlobTool/GlobTool.tsx +0 -119
  194. package/src/tools/GlobTool/prompt.ts +0 -8
  195. package/src/tools/GrepTool/GrepTool.tsx +0 -147
  196. package/src/tools/GrepTool/prompt.ts +0 -11
  197. package/src/tools/MCPTool/MCPTool.tsx +0 -107
  198. package/src/tools/MCPTool/prompt.ts +0 -3
  199. package/src/tools/MemoryReadTool/MemoryReadTool.tsx +0 -127
  200. package/src/tools/MemoryReadTool/prompt.ts +0 -3
  201. package/src/tools/MemoryWriteTool/MemoryWriteTool.tsx +0 -89
  202. package/src/tools/MemoryWriteTool/prompt.ts +0 -3
  203. package/src/tools/MultiEditTool/MultiEditTool.tsx +0 -388
  204. package/src/tools/MultiEditTool/prompt.ts +0 -45
  205. package/src/tools/NotebookEditTool/NotebookEditTool.tsx +0 -298
  206. package/src/tools/NotebookEditTool/prompt.ts +0 -3
  207. package/src/tools/NotebookReadTool/NotebookReadTool.tsx +0 -258
  208. package/src/tools/NotebookReadTool/prompt.ts +0 -3
  209. package/src/tools/StickerRequestTool/StickerRequestTool.tsx +0 -107
  210. package/src/tools/StickerRequestTool/prompt.ts +0 -19
  211. package/src/tools/TaskTool/TaskTool.tsx +0 -438
  212. package/src/tools/TaskTool/constants.ts +0 -1
  213. package/src/tools/TaskTool/prompt.ts +0 -92
  214. package/src/tools/ThinkTool/ThinkTool.tsx +0 -54
  215. package/src/tools/ThinkTool/prompt.ts +0 -12
  216. package/src/tools/TodoWriteTool/TodoWriteTool.tsx +0 -313
  217. package/src/tools/TodoWriteTool/prompt.ts +0 -63
  218. package/src/tools/URLFetcherTool/URLFetcherTool.tsx +0 -178
  219. package/src/tools/URLFetcherTool/cache.ts +0 -55
  220. package/src/tools/URLFetcherTool/htmlToMarkdown.ts +0 -55
  221. package/src/tools/URLFetcherTool/prompt.ts +0 -17
  222. package/src/tools/WebSearchTool/WebSearchTool.tsx +0 -103
  223. package/src/tools/WebSearchTool/prompt.ts +0 -13
  224. package/src/tools/WebSearchTool/searchProviders.ts +0 -66
  225. package/src/tools/lsTool/lsTool.tsx +0 -272
  226. package/src/tools/lsTool/prompt.ts +0 -2
  227. package/src/tools.ts +0 -67
  228. package/src/types/PermissionMode.ts +0 -120
  229. package/src/types/RequestContext.ts +0 -72
  230. package/src/types/common.d.ts +0 -2
  231. package/src/types/conversation.ts +0 -51
  232. package/src/types/logs.ts +0 -58
  233. package/src/types/modelCapabilities.ts +0 -64
  234. package/src/types/notebook.ts +0 -87
  235. package/src/utils/Cursor.ts +0 -436
  236. package/src/utils/PersistentShell.ts +0 -552
  237. package/src/utils/advancedFuzzyMatcher.ts +0 -290
  238. package/src/utils/agentLoader.ts +0 -278
  239. package/src/utils/agentStorage.ts +0 -97
  240. package/src/utils/array.ts +0 -3
  241. package/src/utils/ask.tsx +0 -99
  242. package/src/utils/auth.ts +0 -13
  243. package/src/utils/autoCompactCore.ts +0 -223
  244. package/src/utils/autoUpdater.ts +0 -458
  245. package/src/utils/betas.ts +0 -20
  246. package/src/utils/browser.ts +0 -14
  247. package/src/utils/cleanup.ts +0 -72
  248. package/src/utils/commands.ts +0 -261
  249. package/src/utils/commonUnixCommands.ts +0 -161
  250. package/src/utils/config.ts +0 -945
  251. package/src/utils/conversationRecovery.ts +0 -55
  252. package/src/utils/debugLogger.ts +0 -1235
  253. package/src/utils/diff.ts +0 -42
  254. package/src/utils/env.ts +0 -57
  255. package/src/utils/errors.ts +0 -21
  256. package/src/utils/exampleCommands.ts +0 -109
  257. package/src/utils/execFileNoThrow.ts +0 -51
  258. package/src/utils/expertChatStorage.ts +0 -136
  259. package/src/utils/file.ts +0 -405
  260. package/src/utils/fileRecoveryCore.ts +0 -71
  261. package/src/utils/format.tsx +0 -44
  262. package/src/utils/fuzzyMatcher.ts +0 -328
  263. package/src/utils/generators.ts +0 -62
  264. package/src/utils/git.ts +0 -92
  265. package/src/utils/globalLogger.ts +0 -77
  266. package/src/utils/http.ts +0 -10
  267. package/src/utils/imagePaste.ts +0 -38
  268. package/src/utils/json.ts +0 -13
  269. package/src/utils/log.ts +0 -382
  270. package/src/utils/markdown.ts +0 -213
  271. package/src/utils/messageContextManager.ts +0 -294
  272. package/src/utils/messages.tsx +0 -945
  273. package/src/utils/model.ts +0 -914
  274. package/src/utils/permissions/filesystem.ts +0 -127
  275. package/src/utils/responseState.ts +0 -23
  276. package/src/utils/ripgrep.ts +0 -167
  277. package/src/utils/secureFile.ts +0 -564
  278. package/src/utils/sessionState.ts +0 -49
  279. package/src/utils/state.ts +0 -25
  280. package/src/utils/style.ts +0 -29
  281. package/src/utils/terminal.ts +0 -50
  282. package/src/utils/theme.ts +0 -127
  283. package/src/utils/thinking.ts +0 -144
  284. package/src/utils/todoStorage.ts +0 -431
  285. package/src/utils/tokens.ts +0 -43
  286. package/src/utils/toolExecutionController.ts +0 -163
  287. package/src/utils/unaryLogging.ts +0 -26
  288. package/src/utils/user.ts +0 -37
  289. package/src/utils/validate.ts +0 -165
@@ -1,945 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync } from 'fs'
2
- import { resolve, join } from 'path'
3
- import { cloneDeep, memoize, pick } from 'lodash-es'
4
- import { homedir } from 'os'
5
- import { GLOBAL_CLAUDE_FILE } from './env'
6
- import { getCwd } from './state'
7
- import { randomBytes } from 'crypto'
8
- import { safeParseJSON } from './json'
9
- import { checkGate, logEvent } from '../services/statsig'
10
- import { GATE_USE_EXTERNAL_UPDATER } from '../constants/betas'
11
- import { ConfigParseError } from './errors'
12
- import type { ThemeNames } from './theme'
13
- import { debug as debugLogger } from './debugLogger'
14
- import { getSessionState, setSessionState } from './sessionState'
15
-
16
- export type McpStdioServerConfig = {
17
- type?: 'stdio' // Optional for backwards compatibility
18
- command: string
19
- args: string[]
20
- env?: Record<string, string>
21
- }
22
-
23
- export type McpSSEServerConfig = {
24
- type: 'sse'
25
- url: string
26
- }
27
-
28
- export type McpServerConfig = McpStdioServerConfig | McpSSEServerConfig
29
-
30
- export type ProjectConfig = {
31
- allowedTools: string[]
32
- context: Record<string, string>
33
- contextFiles?: string[]
34
- history: string[]
35
- dontCrawlDirectory?: boolean
36
- enableArchitectTool?: boolean
37
- mcpContextUris: string[]
38
- mcpServers?: Record<string, McpServerConfig>
39
- approvedMcprcServers?: string[]
40
- rejectedMcprcServers?: string[]
41
- lastAPIDuration?: number
42
- lastCost?: number
43
- lastDuration?: number
44
- lastSessionId?: string
45
- exampleFiles?: string[]
46
- exampleFilesGeneratedAt?: number
47
- hasTrustDialogAccepted?: boolean
48
- hasCompletedProjectOnboarding?: boolean
49
- }
50
-
51
- const DEFAULT_PROJECT_CONFIG: ProjectConfig = {
52
- allowedTools: [],
53
- context: {},
54
- history: [],
55
- dontCrawlDirectory: false,
56
- enableArchitectTool: false,
57
- mcpContextUris: [],
58
- mcpServers: {},
59
- approvedMcprcServers: [],
60
- rejectedMcprcServers: [],
61
- hasTrustDialogAccepted: false,
62
- }
63
-
64
- function defaultConfigForProject(projectPath: string): ProjectConfig {
65
- const config = { ...DEFAULT_PROJECT_CONFIG }
66
- if (projectPath === homedir()) {
67
- config.dontCrawlDirectory = true
68
- }
69
- return config
70
- }
71
-
72
- export type AutoUpdaterStatus =
73
- | 'disabled'
74
- | 'enabled'
75
- | 'no_permissions'
76
- | 'not_configured'
77
-
78
- export function isAutoUpdaterStatus(value: string): value is AutoUpdaterStatus {
79
- return ['disabled', 'enabled', 'no_permissions', 'not_configured'].includes(
80
- value as AutoUpdaterStatus,
81
- )
82
- }
83
-
84
- export type NotificationChannel =
85
- | 'iterm2'
86
- | 'terminal_bell'
87
- | 'iterm2_with_bell'
88
- | 'notifications_disabled'
89
-
90
- export type ProviderType =
91
- | 'anthropic'
92
- | 'openai'
93
- | 'mistral'
94
- | 'deepseek'
95
- | 'kimi'
96
- | 'qwen'
97
- | 'glm'
98
- | 'minimax'
99
- | 'baidu-qianfan'
100
- | 'siliconflow'
101
- | 'bigdream'
102
- | 'opendev'
103
- | 'xai'
104
- | 'groq'
105
- | 'gemini'
106
- | 'ollama'
107
- | 'azure'
108
- | 'custom'
109
- | 'custom-openai'
110
-
111
- // New model system types
112
- export type ModelProfile = {
113
- name: string // User-friendly name
114
- provider: ProviderType // Provider type
115
- modelName: string // Primary key - actual model identifier
116
- baseURL?: string // Custom endpoint
117
- apiKey: string
118
- maxTokens: number // Output token limit (for GPT-5, this maps to max_completion_tokens)
119
- contextLength: number // Context window size
120
- reasoningEffort?: 'low' | 'medium' | 'high' | 'minimal' | 'medium'
121
- isActive: boolean // Whether profile is enabled
122
- createdAt: number // Creation timestamp
123
- lastUsed?: number // Last usage timestamp
124
- // 🔥 GPT-5 specific metadata
125
- isGPT5?: boolean // Auto-detected GPT-5 model flag
126
- validationStatus?: 'valid' | 'needs_repair' | 'auto_repaired' // Configuration status
127
- lastValidation?: number // Last validation timestamp
128
- }
129
-
130
- export type ModelPointerType = 'main' | 'task' | 'reasoning' | 'quick'
131
-
132
- export type ModelPointers = {
133
- main: string // Main dialog model ID
134
- task: string // Task tool model ID
135
- reasoning: string // Reasoning model ID
136
- quick: string // Quick model ID
137
- }
138
-
139
- export type AccountInfo = {
140
- accountUuid: string
141
- emailAddress: string
142
- organizationUuid?: string
143
- }
144
-
145
- export type GlobalConfig = {
146
- projects?: Record<string, ProjectConfig>
147
- numStartups: number
148
- autoUpdaterStatus?: AutoUpdaterStatus
149
- userID?: string
150
- theme: ThemeNames
151
- hasCompletedOnboarding?: boolean
152
- // Tracks the last version that reset onboarding, used with MIN_VERSION_REQUIRING_ONBOARDING_RESET
153
- lastOnboardingVersion?: string
154
- // Tracks the last version for which release notes were seen, used for managing release notes
155
- lastReleaseNotesSeen?: string
156
- mcpServers?: Record<string, McpServerConfig>
157
- preferredNotifChannel: NotificationChannel
158
- verbose: boolean
159
- customApiKeyResponses?: {
160
- approved?: string[]
161
- rejected?: string[]
162
- }
163
- primaryProvider?: ProviderType
164
- maxTokens?: number
165
- hasAcknowledgedCostThreshold?: boolean
166
- oauthAccount?: AccountInfo
167
- iterm2KeyBindingInstalled?: boolean // Legacy - keeping for backward compatibility
168
- shiftEnterKeyBindingInstalled?: boolean
169
- proxy?: string
170
- stream?: boolean
171
-
172
- // New model system
173
- modelProfiles?: ModelProfile[] // Model configuration list
174
- modelPointers?: ModelPointers // Model pointer system
175
- defaultModelName?: string // Default model
176
- // Update notifications
177
- lastDismissedUpdateVersion?: string
178
- }
179
-
180
- export const DEFAULT_GLOBAL_CONFIG: GlobalConfig = {
181
- numStartups: 0,
182
- autoUpdaterStatus: 'not_configured',
183
- theme: 'dark' as ThemeNames,
184
- preferredNotifChannel: 'iterm2',
185
- verbose: false,
186
- primaryProvider: 'anthropic' as ProviderType,
187
- customApiKeyResponses: {
188
- approved: [],
189
- rejected: [],
190
- },
191
- stream: true,
192
-
193
- // New model system defaults
194
- modelProfiles: [],
195
- modelPointers: {
196
- main: '',
197
- task: '',
198
- reasoning: '',
199
- quick: '',
200
- },
201
- lastDismissedUpdateVersion: undefined,
202
- }
203
-
204
- export const GLOBAL_CONFIG_KEYS = [
205
- 'autoUpdaterStatus',
206
- 'theme',
207
- 'hasCompletedOnboarding',
208
- 'lastOnboardingVersion',
209
- 'lastReleaseNotesSeen',
210
- 'verbose',
211
- 'customApiKeyResponses',
212
- 'primaryProvider',
213
- 'preferredNotifChannel',
214
- 'shiftEnterKeyBindingInstalled',
215
- 'maxTokens',
216
- ] as const
217
-
218
- export type GlobalConfigKey = (typeof GLOBAL_CONFIG_KEYS)[number]
219
-
220
- export function isGlobalConfigKey(key: string): key is GlobalConfigKey {
221
- return GLOBAL_CONFIG_KEYS.includes(key as GlobalConfigKey)
222
- }
223
-
224
- export const PROJECT_CONFIG_KEYS = [
225
- 'dontCrawlDirectory',
226
- 'enableArchitectTool',
227
- 'hasTrustDialogAccepted',
228
- 'hasCompletedProjectOnboarding',
229
- ] as const
230
-
231
- export type ProjectConfigKey = (typeof PROJECT_CONFIG_KEYS)[number]
232
-
233
- export function checkHasTrustDialogAccepted(): boolean {
234
- let currentPath = getCwd()
235
- const config = getConfig(GLOBAL_CLAUDE_FILE, DEFAULT_GLOBAL_CONFIG)
236
-
237
- while (true) {
238
- const projectConfig = config.projects?.[currentPath]
239
- if (projectConfig?.hasTrustDialogAccepted) {
240
- return true
241
- }
242
- const parentPath = resolve(currentPath, '..')
243
- // Stop if we've reached the root (when parent is same as current)
244
- if (parentPath === currentPath) {
245
- break
246
- }
247
- currentPath = parentPath
248
- }
249
-
250
- return false
251
- }
252
-
253
- // We have to put this test code here because Jest doesn't support mocking ES modules :O
254
- const TEST_GLOBAL_CONFIG_FOR_TESTING: GlobalConfig = {
255
- ...DEFAULT_GLOBAL_CONFIG,
256
- autoUpdaterStatus: 'disabled',
257
- }
258
- const TEST_PROJECT_CONFIG_FOR_TESTING: ProjectConfig = {
259
- ...DEFAULT_PROJECT_CONFIG,
260
- }
261
-
262
- export function isProjectConfigKey(key: string): key is ProjectConfigKey {
263
- return PROJECT_CONFIG_KEYS.includes(key as ProjectConfigKey)
264
- }
265
-
266
- export function saveGlobalConfig(config: GlobalConfig): void {
267
- if (process.env.NODE_ENV === 'test') {
268
- for (const key in config) {
269
- TEST_GLOBAL_CONFIG_FOR_TESTING[key] = config[key]
270
- }
271
- return
272
- }
273
-
274
- // 直接保存配置(无需清除缓存,因为已移除缓存)
275
- saveConfig(
276
- GLOBAL_CLAUDE_FILE,
277
- {
278
- ...config,
279
- projects: getConfig(GLOBAL_CLAUDE_FILE, DEFAULT_GLOBAL_CONFIG).projects,
280
- },
281
- DEFAULT_GLOBAL_CONFIG,
282
- )
283
- }
284
-
285
- // 临时移除缓存,确保总是获取最新配置
286
- export function getGlobalConfig(): GlobalConfig {
287
- if (process.env.NODE_ENV === 'test') {
288
- return TEST_GLOBAL_CONFIG_FOR_TESTING
289
- }
290
- const config = getConfig(GLOBAL_CLAUDE_FILE, DEFAULT_GLOBAL_CONFIG)
291
- return migrateModelProfilesRemoveId(config)
292
- }
293
-
294
- export function getAnthropicApiKey(): null | string {
295
- return process.env.ANTHROPIC_API_KEY || null
296
- }
297
-
298
- export function normalizeApiKeyForConfig(apiKey: string): string {
299
- return apiKey?.slice(-20) ?? ''
300
- }
301
-
302
- export function getCustomApiKeyStatus(
303
- truncatedApiKey: string,
304
- ): 'approved' | 'rejected' | 'new' {
305
- const config = getGlobalConfig()
306
- if (config.customApiKeyResponses?.approved?.includes(truncatedApiKey)) {
307
- return 'approved'
308
- }
309
- if (config.customApiKeyResponses?.rejected?.includes(truncatedApiKey)) {
310
- return 'rejected'
311
- }
312
- return 'new'
313
- }
314
-
315
- function saveConfig<A extends object>(
316
- file: string,
317
- config: A,
318
- defaultConfig: A,
319
- ): void {
320
- // Filter out any values that match the defaults
321
- const filteredConfig = Object.fromEntries(
322
- Object.entries(config).filter(
323
- ([key, value]) =>
324
- JSON.stringify(value) !== JSON.stringify(defaultConfig[key as keyof A]),
325
- ),
326
- )
327
- writeFileSync(file, JSON.stringify(filteredConfig, null, 2), 'utf-8')
328
- }
329
-
330
- // Flag to track if config reading is allowed
331
- let configReadingAllowed = false
332
-
333
- export function enableConfigs(): void {
334
- // Any reads to configuration before this flag is set show an console warning
335
- // to prevent us from adding config reading during module initialization
336
- configReadingAllowed = true
337
- // We only check the global config because currently all the configs share a file
338
- getConfig(
339
- GLOBAL_CLAUDE_FILE,
340
- DEFAULT_GLOBAL_CONFIG,
341
- true /* throw on invalid */,
342
- )
343
- }
344
-
345
- function getConfig<A>(
346
- file: string,
347
- defaultConfig: A,
348
- throwOnInvalid?: boolean,
349
- ): A {
350
- // 简化配置访问逻辑,移除复杂的时序检查
351
-
352
- debugLogger.state('CONFIG_LOAD_START', {
353
- file,
354
- fileExists: String(existsSync(file)),
355
- throwOnInvalid: String(!!throwOnInvalid),
356
- })
357
-
358
- if (!existsSync(file)) {
359
- debugLogger.state('CONFIG_LOAD_DEFAULT', {
360
- file,
361
- reason: 'file_not_exists',
362
- defaultConfigKeys: Object.keys(defaultConfig as object).join(', '),
363
- })
364
- return cloneDeep(defaultConfig)
365
- }
366
-
367
- try {
368
- const fileContent = readFileSync(file, 'utf-8')
369
- debugLogger.state('CONFIG_FILE_READ', {
370
- file,
371
- contentLength: String(fileContent.length),
372
- contentPreview:
373
- fileContent.substring(0, 100) + (fileContent.length > 100 ? '...' : ''),
374
- })
375
-
376
- try {
377
- const parsedConfig = JSON.parse(fileContent)
378
- debugLogger.state('CONFIG_JSON_PARSED', {
379
- file,
380
- parsedKeys: Object.keys(parsedConfig).join(', '),
381
- })
382
-
383
- // Handle backward compatibility - remove logic for deleted fields
384
- const finalConfig = {
385
- ...cloneDeep(defaultConfig),
386
- ...parsedConfig,
387
- }
388
-
389
- debugLogger.state('CONFIG_LOAD_SUCCESS', {
390
- file,
391
- finalConfigKeys: Object.keys(finalConfig as object).join(', '),
392
- })
393
-
394
- return finalConfig
395
- } catch (error) {
396
- // Throw a ConfigParseError with the file path and default config
397
- const errorMessage =
398
- error instanceof Error ? error.message : String(error)
399
-
400
- debugLogger.error('CONFIG_JSON_PARSE_ERROR', {
401
- file,
402
- errorMessage,
403
- errorType:
404
- error instanceof Error ? error.constructor.name : typeof error,
405
- contentLength: String(fileContent.length),
406
- })
407
-
408
- throw new ConfigParseError(errorMessage, file, defaultConfig)
409
- }
410
- } catch (error: unknown) {
411
- // Re-throw ConfigParseError if throwOnInvalid is true
412
- if (error instanceof ConfigParseError && throwOnInvalid) {
413
- debugLogger.error('CONFIG_PARSE_ERROR_RETHROWN', {
414
- file,
415
- throwOnInvalid: String(throwOnInvalid),
416
- errorMessage: error.message,
417
- })
418
- throw error
419
- }
420
-
421
- debugLogger.warn('CONFIG_FALLBACK_TO_DEFAULT', {
422
- file,
423
- errorType: error instanceof Error ? error.constructor.name : typeof error,
424
- errorMessage: error instanceof Error ? error.message : String(error),
425
- action: 'using_default_config',
426
- })
427
-
428
- return cloneDeep(defaultConfig)
429
- }
430
- }
431
-
432
- export function getCurrentProjectConfig(): ProjectConfig {
433
- if (process.env.NODE_ENV === 'test') {
434
- return TEST_PROJECT_CONFIG_FOR_TESTING
435
- }
436
-
437
- const absolutePath = resolve(getCwd())
438
- const config = getConfig(GLOBAL_CLAUDE_FILE, DEFAULT_GLOBAL_CONFIG)
439
-
440
- if (!config.projects) {
441
- return defaultConfigForProject(absolutePath)
442
- }
443
-
444
- const projectConfig =
445
- config.projects[absolutePath] ?? defaultConfigForProject(absolutePath)
446
- // Not sure how this became a string
447
- // TODO: Fix upstream
448
- if (typeof projectConfig.allowedTools === 'string') {
449
- projectConfig.allowedTools =
450
- (safeParseJSON(projectConfig.allowedTools) as string[]) ?? []
451
- }
452
- return projectConfig
453
- }
454
-
455
- export function saveCurrentProjectConfig(projectConfig: ProjectConfig): void {
456
- if (process.env.NODE_ENV === 'test') {
457
- for (const key in projectConfig) {
458
- TEST_PROJECT_CONFIG_FOR_TESTING[key] = projectConfig[key]
459
- }
460
- return
461
- }
462
- const config = getConfig(GLOBAL_CLAUDE_FILE, DEFAULT_GLOBAL_CONFIG)
463
- saveConfig(
464
- GLOBAL_CLAUDE_FILE,
465
- {
466
- ...config,
467
- projects: {
468
- ...config.projects,
469
- [resolve(getCwd())]: projectConfig,
470
- },
471
- },
472
- DEFAULT_GLOBAL_CONFIG,
473
- )
474
- }
475
-
476
- export async function isAutoUpdaterDisabled(): Promise<boolean> {
477
- const useExternalUpdater = await checkGate(GATE_USE_EXTERNAL_UPDATER)
478
- return (
479
- useExternalUpdater || getGlobalConfig().autoUpdaterStatus === 'disabled'
480
- )
481
- }
482
-
483
- export const TEST_MCPRC_CONFIG_FOR_TESTING: Record<string, McpServerConfig> = {}
484
-
485
- export function clearMcprcConfigForTesting(): void {
486
- if (process.env.NODE_ENV === 'test') {
487
- Object.keys(TEST_MCPRC_CONFIG_FOR_TESTING).forEach(key => {
488
- delete TEST_MCPRC_CONFIG_FOR_TESTING[key]
489
- })
490
- }
491
- }
492
-
493
- export function addMcprcServerForTesting(
494
- name: string,
495
- server: McpServerConfig,
496
- ): void {
497
- if (process.env.NODE_ENV === 'test') {
498
- TEST_MCPRC_CONFIG_FOR_TESTING[name] = server
499
- }
500
- }
501
-
502
- export function removeMcprcServerForTesting(name: string): void {
503
- if (process.env.NODE_ENV === 'test') {
504
- if (!TEST_MCPRC_CONFIG_FOR_TESTING[name]) {
505
- throw new Error(`No MCP server found with name: ${name} in .mcprc`)
506
- }
507
- delete TEST_MCPRC_CONFIG_FOR_TESTING[name]
508
- }
509
- }
510
-
511
- export const getMcprcConfig = memoize(
512
- (): Record<string, McpServerConfig> => {
513
- if (process.env.NODE_ENV === 'test') {
514
- return TEST_MCPRC_CONFIG_FOR_TESTING
515
- }
516
-
517
- const mcprcPath = join(getCwd(), '.mcprc')
518
- if (!existsSync(mcprcPath)) {
519
- return {}
520
- }
521
-
522
- try {
523
- const mcprcContent = readFileSync(mcprcPath, 'utf-8')
524
- const config = safeParseJSON(mcprcContent)
525
- if (config && typeof config === 'object') {
526
- logEvent('tengu_mcprc_found', {
527
- numServers: Object.keys(config).length.toString(),
528
- })
529
- return config as Record<string, McpServerConfig>
530
- }
531
- } catch {
532
- // Ignore errors reading/parsing .mcprc (they're logged in safeParseJSON)
533
- }
534
- return {}
535
- },
536
- // This function returns the same value as long as the cwd and mcprc file content remain the same
537
- () => {
538
- const cwd = getCwd()
539
- const mcprcPath = join(cwd, '.mcprc')
540
- if (existsSync(mcprcPath)) {
541
- try {
542
- const stat = readFileSync(mcprcPath, 'utf-8')
543
- return `${cwd}:${stat}`
544
- } catch {
545
- return cwd
546
- }
547
- }
548
- return cwd
549
- },
550
- )
551
-
552
- export function getOrCreateUserID(): string {
553
- const config = getGlobalConfig()
554
- if (config.userID) {
555
- return config.userID
556
- }
557
-
558
- const userID = randomBytes(32).toString('hex')
559
- saveGlobalConfig({ ...config, userID })
560
- return userID
561
- }
562
-
563
- export function getConfigForCLI(key: string, global: boolean): unknown {
564
- logEvent('tengu_config_get', {
565
- key,
566
- global: global?.toString() ?? 'false',
567
- })
568
- if (global) {
569
- if (!isGlobalConfigKey(key)) {
570
- console.error(
571
- `Error: '${key}' is not a valid config key. Valid keys are: ${GLOBAL_CONFIG_KEYS.join(', ')}`,
572
- )
573
- process.exit(1)
574
- }
575
- return getGlobalConfig()[key]
576
- } else {
577
- if (!isProjectConfigKey(key)) {
578
- console.error(
579
- `Error: '${key}' is not a valid config key. Valid keys are: ${PROJECT_CONFIG_KEYS.join(', ')}`,
580
- )
581
- process.exit(1)
582
- }
583
- return getCurrentProjectConfig()[key]
584
- }
585
- }
586
-
587
- export function setConfigForCLI(
588
- key: string,
589
- value: unknown,
590
- global: boolean,
591
- ): void {
592
- logEvent('tengu_config_set', {
593
- key,
594
- global: global?.toString() ?? 'false',
595
- })
596
- if (global) {
597
- if (!isGlobalConfigKey(key)) {
598
- console.error(
599
- `Error: Cannot set '${key}'. Only these keys can be modified: ${GLOBAL_CONFIG_KEYS.join(', ')}`,
600
- )
601
- process.exit(1)
602
- }
603
-
604
- if (key === 'autoUpdaterStatus' && !isAutoUpdaterStatus(value as string)) {
605
- console.error(
606
- `Error: Invalid value for autoUpdaterStatus. Must be one of: disabled, enabled, no_permissions, not_configured`,
607
- )
608
- process.exit(1)
609
- }
610
-
611
- const currentConfig = getGlobalConfig()
612
- saveGlobalConfig({
613
- ...currentConfig,
614
- [key]: value,
615
- })
616
- } else {
617
- if (!isProjectConfigKey(key)) {
618
- console.error(
619
- `Error: Cannot set '${key}'. Only these keys can be modified: ${PROJECT_CONFIG_KEYS.join(', ')}. Did you mean --global?`,
620
- )
621
- process.exit(1)
622
- }
623
- const currentConfig = getCurrentProjectConfig()
624
- saveCurrentProjectConfig({
625
- ...currentConfig,
626
- [key]: value,
627
- })
628
- }
629
- // Wait for the output to be flushed, to avoid clearing the screen.
630
- setTimeout(() => {
631
- // Without this we hang indefinitely.
632
- process.exit(0)
633
- }, 100)
634
- }
635
-
636
- export function deleteConfigForCLI(key: string, global: boolean): void {
637
- logEvent('tengu_config_delete', {
638
- key,
639
- global: global?.toString() ?? 'false',
640
- })
641
- if (global) {
642
- if (!isGlobalConfigKey(key)) {
643
- console.error(
644
- `Error: Cannot delete '${key}'. Only these keys can be modified: ${GLOBAL_CONFIG_KEYS.join(', ')}`,
645
- )
646
- process.exit(1)
647
- }
648
- const currentConfig = getGlobalConfig()
649
- delete currentConfig[key]
650
- saveGlobalConfig(currentConfig)
651
- } else {
652
- if (!isProjectConfigKey(key)) {
653
- console.error(
654
- `Error: Cannot delete '${key}'. Only these keys can be modified: ${PROJECT_CONFIG_KEYS.join(', ')}. Did you mean --global?`,
655
- )
656
- process.exit(1)
657
- }
658
- const currentConfig = getCurrentProjectConfig()
659
- delete currentConfig[key]
660
- saveCurrentProjectConfig(currentConfig)
661
- }
662
- }
663
-
664
- export function listConfigForCLI(global: true): GlobalConfig
665
- export function listConfigForCLI(global: false): ProjectConfig
666
- export function listConfigForCLI(global: boolean): object {
667
- logEvent('tengu_config_list', {
668
- global: global?.toString() ?? 'false',
669
- })
670
- if (global) {
671
- const currentConfig = pick(getGlobalConfig(), GLOBAL_CONFIG_KEYS)
672
- return currentConfig
673
- } else {
674
- return pick(getCurrentProjectConfig(), PROJECT_CONFIG_KEYS)
675
- }
676
- }
677
-
678
- export function getOpenAIApiKey(): string | undefined {
679
- return process.env.OPENAI_API_KEY
680
- }
681
-
682
- // Configuration migration utility functions
683
- function migrateModelProfilesRemoveId(config: GlobalConfig): GlobalConfig {
684
- if (!config.modelProfiles) return config
685
-
686
- // 1. Remove id field from ModelProfile objects and build ID to modelName mapping
687
- const idToModelNameMap = new Map<string, string>()
688
- const migratedProfiles = config.modelProfiles.map(profile => {
689
- // Build mapping before removing id field
690
- if ((profile as any).id && profile.modelName) {
691
- idToModelNameMap.set((profile as any).id, profile.modelName)
692
- }
693
-
694
- // Remove id field, keep everything else
695
- const { id, ...profileWithoutId } = profile as any
696
- return profileWithoutId as ModelProfile
697
- })
698
-
699
- // 2. Migrate ModelPointers from IDs to modelNames
700
- const migratedPointers: ModelPointers = {
701
- main: '',
702
- task: '',
703
- reasoning: '',
704
- quick: '',
705
- }
706
-
707
- if (config.modelPointers) {
708
- Object.entries(config.modelPointers).forEach(([pointer, value]) => {
709
- if (value) {
710
- // If value looks like an old ID (model_xxx), map it to modelName
711
- const modelName = idToModelNameMap.get(value) || value
712
- migratedPointers[pointer as ModelPointerType] = modelName
713
- }
714
- })
715
- }
716
-
717
- // 3. Migrate legacy config fields
718
- let defaultModelName: string | undefined
719
- if ((config as any).defaultModelId) {
720
- defaultModelName =
721
- idToModelNameMap.get((config as any).defaultModelId) ||
722
- (config as any).defaultModelId
723
- } else if ((config as any).defaultModelName) {
724
- defaultModelName = (config as any).defaultModelName
725
- }
726
-
727
- // 4. Remove legacy fields and return migrated config
728
- const migratedConfig = { ...config }
729
- delete (migratedConfig as any).defaultModelId
730
- delete (migratedConfig as any).currentSelectedModelId
731
- delete (migratedConfig as any).mainAgentModelId
732
- delete (migratedConfig as any).taskToolModelId
733
-
734
- return {
735
- ...migratedConfig,
736
- modelProfiles: migratedProfiles,
737
- modelPointers: migratedPointers,
738
- defaultModelName,
739
- }
740
- }
741
-
742
- // New model system utility functions
743
-
744
- export function setAllPointersToModel(modelName: string): void {
745
- const config = getGlobalConfig()
746
- const updatedConfig = {
747
- ...config,
748
- modelPointers: {
749
- main: modelName,
750
- task: modelName,
751
- reasoning: modelName,
752
- quick: modelName,
753
- },
754
- defaultModelName: modelName,
755
- }
756
- saveGlobalConfig(updatedConfig)
757
- }
758
-
759
- export function setModelPointer(
760
- pointer: ModelPointerType,
761
- modelName: string,
762
- ): void {
763
- const config = getGlobalConfig()
764
- const updatedConfig = {
765
- ...config,
766
- modelPointers: {
767
- ...config.modelPointers,
768
- [pointer]: modelName,
769
- },
770
- }
771
- saveGlobalConfig(updatedConfig)
772
-
773
- // 🔧 Fix: Force ModelManager reload after config change
774
- // Import here to avoid circular dependency
775
- import('./model').then(({ reloadModelManager }) => {
776
- reloadModelManager()
777
- })
778
- }
779
-
780
- // 🔥 GPT-5 Configuration Validation and Auto-Repair Functions
781
-
782
- /**
783
- * Check if a model name represents a GPT-5 model
784
- */
785
- export function isGPT5ModelName(modelName: string): boolean {
786
- if (!modelName || typeof modelName !== 'string') return false
787
- const lowerName = modelName.toLowerCase()
788
- return lowerName.startsWith('gpt-5') || lowerName.includes('gpt-5')
789
- }
790
-
791
- /**
792
- * Validate and auto-repair GPT-5 model configuration
793
- */
794
- export function validateAndRepairGPT5Profile(profile: ModelProfile): ModelProfile {
795
- const isGPT5 = isGPT5ModelName(profile.modelName)
796
- const now = Date.now()
797
-
798
- // Create a working copy
799
- const repairedProfile: ModelProfile = { ...profile }
800
- let wasRepaired = false
801
-
802
- // 🔧 Set GPT-5 detection flag
803
- if (isGPT5 !== profile.isGPT5) {
804
- repairedProfile.isGPT5 = isGPT5
805
- wasRepaired = true
806
- }
807
-
808
- if (isGPT5) {
809
- // 🔧 GPT-5 Parameter Validation and Repair
810
-
811
- // 1. Reasoning effort validation
812
- const validReasoningEfforts = ['minimal', 'low', 'medium', 'high']
813
- if (!profile.reasoningEffort || !validReasoningEfforts.includes(profile.reasoningEffort)) {
814
- repairedProfile.reasoningEffort = 'medium' // Default for coding tasks
815
- wasRepaired = true
816
- console.log(`🔧 GPT-5 Config: Set reasoning effort to 'medium' for ${profile.modelName}`)
817
- }
818
-
819
- // 2. Context length validation (GPT-5 models typically have 128k context)
820
- if (profile.contextLength < 128000) {
821
- repairedProfile.contextLength = 128000
822
- wasRepaired = true
823
- console.log(`🔧 GPT-5 Config: Updated context length to 128k for ${profile.modelName}`)
824
- }
825
-
826
- // 3. Output tokens validation (reasonable defaults for GPT-5)
827
- if (profile.maxTokens < 4000) {
828
- repairedProfile.maxTokens = 8192 // Good default for coding tasks
829
- wasRepaired = true
830
- console.log(`🔧 GPT-5 Config: Updated max tokens to 8192 for ${profile.modelName}`)
831
- }
832
-
833
- // 4. Provider validation
834
- if (profile.provider !== 'openai' && profile.provider !== 'custom-openai' && profile.provider !== 'azure') {
835
- console.warn(`⚠️ GPT-5 Config: Unexpected provider '${profile.provider}' for GPT-5 model ${profile.modelName}. Consider using 'openai' or 'custom-openai'.`)
836
- }
837
-
838
- // 5. Base URL validation for official models
839
- if (profile.modelName.includes('gpt-5') && !profile.baseURL) {
840
- repairedProfile.baseURL = 'https://api.openai.com/v1'
841
- wasRepaired = true
842
- console.log(`🔧 GPT-5 Config: Set default base URL for ${profile.modelName}`)
843
- }
844
- }
845
-
846
- // Update validation metadata
847
- repairedProfile.validationStatus = wasRepaired ? 'auto_repaired' : 'valid'
848
- repairedProfile.lastValidation = now
849
-
850
- if (wasRepaired) {
851
- console.log(`✅ GPT-5 Config: Auto-repaired configuration for ${profile.modelName}`)
852
- }
853
-
854
- return repairedProfile
855
- }
856
-
857
- /**
858
- * Validate and repair all GPT-5 profiles in the global configuration
859
- */
860
- export function validateAndRepairAllGPT5Profiles(): { repaired: number; total: number } {
861
- const config = getGlobalConfig()
862
- if (!config.modelProfiles) {
863
- return { repaired: 0, total: 0 }
864
- }
865
-
866
- let repairCount = 0
867
- const repairedProfiles = config.modelProfiles.map(profile => {
868
- const repairedProfile = validateAndRepairGPT5Profile(profile)
869
- if (repairedProfile.validationStatus === 'auto_repaired') {
870
- repairCount++
871
- }
872
- return repairedProfile
873
- })
874
-
875
- // Save the repaired configuration
876
- if (repairCount > 0) {
877
- const updatedConfig = {
878
- ...config,
879
- modelProfiles: repairedProfiles,
880
- }
881
- saveGlobalConfig(updatedConfig)
882
- console.log(`🔧 GPT-5 Config: Auto-repaired ${repairCount} model profiles`)
883
- }
884
-
885
- return { repaired: repairCount, total: config.modelProfiles.length }
886
- }
887
-
888
- /**
889
- * Get GPT-5 configuration recommendations for a specific model
890
- */
891
- export function getGPT5ConfigRecommendations(modelName: string): Partial<ModelProfile> {
892
- if (!isGPT5ModelName(modelName)) {
893
- return {}
894
- }
895
-
896
- const recommendations: Partial<ModelProfile> = {
897
- contextLength: 128000, // GPT-5 standard context length
898
- maxTokens: 8192, // Good default for coding tasks
899
- reasoningEffort: 'medium', // Balanced for most coding tasks
900
- isGPT5: true,
901
- }
902
-
903
- // Model-specific optimizations
904
- if (modelName.includes('gpt-5-mini')) {
905
- recommendations.maxTokens = 4096 // Smaller default for mini
906
- recommendations.reasoningEffort = 'low' // Faster for simple tasks
907
- } else if (modelName.includes('gpt-5-nano')) {
908
- recommendations.maxTokens = 2048 // Even smaller for nano
909
- recommendations.reasoningEffort = 'minimal' // Fastest option
910
- }
911
-
912
- return recommendations
913
- }
914
-
915
- /**
916
- * Create a properly configured GPT-5 model profile
917
- */
918
- export function createGPT5ModelProfile(
919
- name: string,
920
- modelName: string,
921
- apiKey: string,
922
- baseURL?: string,
923
- provider: ProviderType = 'openai'
924
- ): ModelProfile {
925
- const recommendations = getGPT5ConfigRecommendations(modelName)
926
-
927
- const profile: ModelProfile = {
928
- name,
929
- provider,
930
- modelName,
931
- baseURL: baseURL || 'https://api.openai.com/v1',
932
- apiKey,
933
- maxTokens: recommendations.maxTokens || 8192,
934
- contextLength: recommendations.contextLength || 128000,
935
- reasoningEffort: recommendations.reasoningEffort || 'medium',
936
- isActive: true,
937
- createdAt: Date.now(),
938
- isGPT5: true,
939
- validationStatus: 'valid',
940
- lastValidation: Date.now(),
941
- }
942
-
943
- console.log(`✅ Created GPT-5 model profile: ${name} (${modelName})`)
944
- return profile
945
- }