@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,914 +0,0 @@
1
- import { memoize } from 'lodash-es'
2
- import { getDynamicConfig, getExperimentValue } from '../services/statsig'
3
- import { logError } from './log'
4
- import {
5
- getGlobalConfig,
6
- ModelProfile,
7
- ModelPointerType,
8
- saveGlobalConfig,
9
- } from './config'
10
-
11
- export const USE_BEDROCK = !!process.env.CLAUDE_CODE_USE_BEDROCK
12
- export const USE_VERTEX = !!process.env.CLAUDE_CODE_USE_VERTEX
13
-
14
- export interface ModelConfig {
15
- bedrock: string
16
- vertex: string
17
- firstParty: string
18
- }
19
-
20
- const DEFAULT_MODEL_CONFIG: ModelConfig = {
21
- bedrock: 'us.anthropic.claude-3-7-sonnet-20250219-v1:0',
22
- vertex: 'claude-3-7-sonnet@20250219',
23
- firstParty: 'claude-sonnet-4-20250514',
24
- }
25
-
26
- /**
27
- * Helper to get the model config from statsig or defaults
28
- * Relies on the built-in caching from StatsigClient
29
- */
30
- async function getModelConfig(): Promise<ModelConfig> {
31
- try {
32
- return await getDynamicConfig<ModelConfig>(
33
- 'tengu-capable-model-config',
34
- DEFAULT_MODEL_CONFIG,
35
- )
36
- } catch (error) {
37
- logError(error)
38
- return DEFAULT_MODEL_CONFIG
39
- }
40
- }
41
-
42
- export const getSlowAndCapableModel = memoize(async (): Promise<string> => {
43
- const config = await getGlobalConfig()
44
-
45
- // Use ModelManager for proper model resolution
46
- const modelManager = new ModelManager(config)
47
- const model = modelManager.getMainAgentModel()
48
-
49
- if (model) {
50
- return model
51
- }
52
-
53
- // Final fallback to default model
54
- const modelConfig = await getModelConfig()
55
- if (USE_BEDROCK) return modelConfig.bedrock
56
- if (USE_VERTEX) return modelConfig.vertex
57
- return modelConfig.firstParty
58
- })
59
-
60
- export async function isDefaultSlowAndCapableModel(): Promise<boolean> {
61
- return (
62
- !process.env.ANTHROPIC_MODEL ||
63
- process.env.ANTHROPIC_MODEL === (await getSlowAndCapableModel())
64
- )
65
- }
66
-
67
- /**
68
- * Get the region for a specific Vertex model
69
- * Checks for hardcoded model-specific environment variables first,
70
- * then falls back to CLOUD_ML_REGION env var or default region
71
- */
72
- export function getVertexRegionForModel(
73
- model: string | undefined,
74
- ): string | undefined {
75
- if (model?.startsWith('claude-3-5-haiku')) {
76
- return process.env.VERTEX_REGION_CLAUDE_3_5_HAIKU
77
- } else if (model?.startsWith('claude-3-5-sonnet')) {
78
- return process.env.VERTEX_REGION_CLAUDE_3_5_SONNET
79
- } else if (model?.startsWith('claude-3-7-sonnet')) {
80
- return process.env.VERTEX_REGION_CLAUDE_3_7_SONNET
81
- }
82
- }
83
-
84
- /**
85
- * Comprehensive ModelManager class for centralized model selection and management.
86
- * Provides a clean interface for model selection across the application.
87
- */
88
- export class ModelManager {
89
- private config: any // Using any to handle legacy properties
90
- private modelProfiles: ModelProfile[]
91
-
92
- constructor(config: any) {
93
- this.config = config
94
- this.modelProfiles = config.modelProfiles || []
95
- }
96
-
97
- /**
98
- * Get the current terminal model (for interactive CLI sessions)
99
- */
100
- getCurrentModel(): string | null {
101
- // Use main pointer from new ModelProfile system
102
- const mainModelName = this.config.modelPointers?.main
103
- if (mainModelName) {
104
- const profile = this.findModelProfile(mainModelName)
105
- if (profile && profile.isActive) {
106
- return profile.modelName
107
- }
108
- }
109
-
110
- // Fallback to main agent model
111
- return this.getMainAgentModel()
112
- }
113
-
114
- /**
115
- * Get the main agent default model (for non-terminal mode and MCP calls)
116
- */
117
- getMainAgentModel(): string | null {
118
- // Use main pointer from new ModelProfile system
119
- const mainModelName = this.config.modelPointers?.main
120
- if (mainModelName) {
121
- const profile = this.findModelProfile(mainModelName)
122
- if (profile && profile.isActive) {
123
- return profile.modelName
124
- }
125
- }
126
-
127
- // Fallback to first active profile
128
- const activeProfile = this.modelProfiles.find(p => p.isActive)
129
- if (activeProfile) {
130
- return activeProfile.modelName
131
- }
132
-
133
- return null
134
- }
135
-
136
- /**
137
- * Get the task tool default model (for Task tool sub-agents)
138
- */
139
- getTaskToolModel(): string | null {
140
- // Use task pointer from new ModelProfile system
141
- const taskModelName = this.config.modelPointers?.task
142
- if (taskModelName) {
143
- const profile = this.findModelProfile(taskModelName)
144
- if (profile && profile.isActive) {
145
- return profile.modelName
146
- }
147
- }
148
-
149
- // Fallback to main agent model
150
- return this.getMainAgentModel()
151
- }
152
-
153
- /**
154
- * Switch to the next available model with simple context overflow handling
155
- * If target model can't handle current context, shows warning and reverts after delay
156
- *
157
- * @param currentContextTokens - Current conversation token count for validation
158
- * @returns Object with model name and context status information
159
- */
160
- switchToNextModelWithContextCheck(currentContextTokens: number = 0): {
161
- success: boolean
162
- modelName: string | null
163
- previousModelName: string | null
164
- contextOverflow: boolean
165
- usagePercentage: number
166
- } {
167
- // Use ALL configured models, not just active ones
168
- const allProfiles = this.getAllConfiguredModels()
169
- if (allProfiles.length === 0) {
170
- return {
171
- success: false,
172
- modelName: null,
173
- previousModelName: null,
174
- contextOverflow: false,
175
- usagePercentage: 0,
176
- }
177
- }
178
-
179
- // Sort by createdAt for consistent cycling order (don't use lastUsed)
180
- // Using lastUsed causes the order to change each time, preventing proper cycling
181
- allProfiles.sort((a, b) => {
182
- return a.createdAt - b.createdAt // Oldest first for consistent order
183
- })
184
-
185
- const currentMainModelName = this.config.modelPointers?.main
186
- const currentModel = currentMainModelName
187
- ? this.findModelProfile(currentMainModelName)
188
- : null
189
- const previousModelName = currentModel?.name || null
190
-
191
- if (!currentMainModelName) {
192
- // No current main model, select first available (activate if needed)
193
- const firstModel = allProfiles[0]
194
- if (!firstModel.isActive) {
195
- firstModel.isActive = true
196
- }
197
- this.setPointer('main', firstModel.modelName)
198
- this.updateLastUsed(firstModel.modelName)
199
-
200
- const analysis = this.analyzeContextCompatibility(
201
- firstModel,
202
- currentContextTokens,
203
- )
204
- return {
205
- success: true,
206
- modelName: firstModel.name,
207
- previousModelName: null,
208
- contextOverflow: !analysis.compatible,
209
- usagePercentage: analysis.usagePercentage,
210
- }
211
- }
212
-
213
- // Find current model index in ALL models
214
- const currentIndex = allProfiles.findIndex(
215
- p => p.modelName === currentMainModelName,
216
- )
217
- if (currentIndex === -1) {
218
- // Current model not found, select first available (activate if needed)
219
- const firstModel = allProfiles[0]
220
- if (!firstModel.isActive) {
221
- firstModel.isActive = true
222
- }
223
- this.setPointer('main', firstModel.modelName)
224
- this.updateLastUsed(firstModel.modelName)
225
-
226
- const analysis = this.analyzeContextCompatibility(
227
- firstModel,
228
- currentContextTokens,
229
- )
230
- return {
231
- success: true,
232
- modelName: firstModel.name,
233
- previousModelName,
234
- contextOverflow: !analysis.compatible,
235
- usagePercentage: analysis.usagePercentage,
236
- }
237
- }
238
-
239
- // Check if only one model is available
240
- if (allProfiles.length === 1) {
241
- return {
242
- success: false,
243
- modelName: null,
244
- previousModelName,
245
- contextOverflow: false,
246
- usagePercentage: 0,
247
- }
248
- }
249
-
250
- // Get next model in cycle (from ALL models)
251
- const nextIndex = (currentIndex + 1) % allProfiles.length
252
- const nextModel = allProfiles[nextIndex]
253
-
254
- // Activate the model if it's not already active
255
- const wasInactive = !nextModel.isActive
256
- if (!nextModel.isActive) {
257
- nextModel.isActive = true
258
- }
259
-
260
- // Analyze context compatibility for next model
261
- const analysis = this.analyzeContextCompatibility(
262
- nextModel,
263
- currentContextTokens,
264
- )
265
-
266
- // Always switch to next model, but return context status
267
- this.setPointer('main', nextModel.modelName)
268
- this.updateLastUsed(nextModel.modelName)
269
-
270
- // Save configuration if we activated a new model
271
- if (wasInactive) {
272
- this.saveConfig()
273
- }
274
-
275
- return {
276
- success: true,
277
- modelName: nextModel.name,
278
- previousModelName,
279
- contextOverflow: !analysis.compatible,
280
- usagePercentage: analysis.usagePercentage,
281
- }
282
- }
283
-
284
- /**
285
- * Simple model switching for UI components (compatible interface)
286
- * @param currentContextTokens - Current conversation token count for validation
287
- * @returns Compatible interface for PromptInput component
288
- */
289
- switchToNextModel(currentContextTokens: number = 0): {
290
- success: boolean
291
- modelName: string | null
292
- blocked?: boolean
293
- message?: string
294
- } {
295
- // Use the enhanced context check method for consistency
296
- const result = this.switchToNextModelWithContextCheck(currentContextTokens)
297
-
298
- if (!result.success) {
299
- const allModels = this.getAllConfiguredModels()
300
- if (allModels.length === 0) {
301
- return {
302
- success: false,
303
- modelName: null,
304
- blocked: false,
305
- message: '❌ No models configured. Use /model to add models.',
306
- }
307
- } else if (allModels.length === 1) {
308
- return {
309
- success: false,
310
- modelName: null,
311
- blocked: false,
312
- message: `⚠️ Only one model configured (${allModels[0].modelName}). Use /model to add more models for switching.`,
313
- }
314
- }
315
- }
316
-
317
- // Convert the detailed result to the simple interface
318
- const currentModel = this.findModelProfile(this.config.modelPointers?.main)
319
- const allModels = this.getAllConfiguredModels()
320
- const currentIndex = allModels.findIndex(m => m.modelName === currentModel?.modelName)
321
- const totalModels = allModels.length
322
-
323
- return {
324
- success: result.success,
325
- modelName: result.modelName,
326
- blocked: result.contextOverflow,
327
- message: result.success
328
- ? result.contextOverflow
329
- ? `⚠️ Context usage: ${result.usagePercentage.toFixed(1)}% - ${result.modelName}`
330
- : `✅ Switched to ${result.modelName} (${currentIndex + 1}/${totalModels})${currentModel?.provider ? ` [${currentModel.provider}]` : ''}`
331
- : `❌ Failed to switch models`,
332
- }
333
- }
334
-
335
- /**
336
- * Revert to previous model (used when context overflow requires rollback)
337
- */
338
- revertToPreviousModel(previousModelName: string): boolean {
339
- const previousModel = this.modelProfiles.find(
340
- p => p.name === previousModelName && p.isActive,
341
- )
342
- if (!previousModel) {
343
- return false
344
- }
345
-
346
- this.setPointer('main', previousModel.modelName)
347
- this.updateLastUsed(previousModel.modelName)
348
- return true
349
- }
350
-
351
- /**
352
- * Enhanced context validation with different severity levels
353
- */
354
- analyzeContextCompatibility(
355
- model: ModelProfile,
356
- contextTokens: number,
357
- ): {
358
- compatible: boolean
359
- severity: 'safe' | 'warning' | 'critical'
360
- usagePercentage: number
361
- recommendation: string
362
- } {
363
- const usableContext = Math.floor(model.contextLength * 0.8) // Reserve 20% for output
364
- const usagePercentage = (contextTokens / usableContext) * 100
365
-
366
- if (usagePercentage <= 70) {
367
- return {
368
- compatible: true,
369
- severity: 'safe',
370
- usagePercentage,
371
- recommendation: 'Full context preserved',
372
- }
373
- } else if (usagePercentage <= 90) {
374
- return {
375
- compatible: true,
376
- severity: 'warning',
377
- usagePercentage,
378
- recommendation: 'Context usage high, consider compression',
379
- }
380
- } else {
381
- return {
382
- compatible: false,
383
- severity: 'critical',
384
- usagePercentage,
385
- recommendation: 'Auto-compression or message truncation required',
386
- }
387
- }
388
- }
389
-
390
- /**
391
- * Switch to next model with enhanced context analysis
392
- */
393
- switchToNextModelWithAnalysis(currentContextTokens: number = 0): {
394
- modelName: string | null
395
- contextAnalysis: ReturnType<typeof this.analyzeContextCompatibility> | null
396
- requiresCompression: boolean
397
- estimatedTokensAfterSwitch: number
398
- } {
399
- const result = this.switchToNextModel(currentContextTokens)
400
-
401
- if (!result.success || !result.modelName) {
402
- return {
403
- modelName: null,
404
- contextAnalysis: null,
405
- requiresCompression: false,
406
- estimatedTokensAfterSwitch: 0,
407
- }
408
- }
409
-
410
- const newModel = this.getModel('main')
411
- if (!newModel) {
412
- return {
413
- modelName: result.modelName,
414
- contextAnalysis: null,
415
- requiresCompression: false,
416
- estimatedTokensAfterSwitch: currentContextTokens,
417
- }
418
- }
419
-
420
- const analysis = this.analyzeContextCompatibility(
421
- newModel,
422
- currentContextTokens,
423
- )
424
-
425
- return {
426
- modelName: result.modelName,
427
- contextAnalysis: analysis,
428
- requiresCompression: analysis.severity === 'critical',
429
- estimatedTokensAfterSwitch: currentContextTokens,
430
- }
431
- }
432
-
433
- /**
434
- * Check if a model can handle the given context size (legacy method)
435
- */
436
- canModelHandleContext(model: ModelProfile, contextTokens: number): boolean {
437
- const analysis = this.analyzeContextCompatibility(model, contextTokens)
438
- return analysis.compatible
439
- }
440
-
441
- /**
442
- * Find the first model that can handle the given context size
443
- */
444
- findModelWithSufficientContext(
445
- models: ModelProfile[],
446
- contextTokens: number,
447
- ): ModelProfile | null {
448
- return (
449
- models.find(model => this.canModelHandleContext(model, contextTokens)) ||
450
- null
451
- )
452
- }
453
-
454
- /**
455
- * Unified model getter for different contexts
456
- */
457
- getModelForContext(
458
- contextType: 'terminal' | 'main-agent' | 'task-tool',
459
- ): string | null {
460
- switch (contextType) {
461
- case 'terminal':
462
- return this.getCurrentModel()
463
- case 'main-agent':
464
- return this.getMainAgentModel()
465
- case 'task-tool':
466
- return this.getTaskToolModel()
467
- default:
468
- return this.getMainAgentModel()
469
- }
470
- }
471
-
472
- /**
473
- * Get all active model profiles
474
- */
475
- getActiveModelProfiles(): ModelProfile[] {
476
- return this.modelProfiles.filter(p => p.isActive)
477
- }
478
-
479
- /**
480
- * Check if any models are configured
481
- */
482
- hasConfiguredModels(): boolean {
483
- return this.getActiveModelProfiles().length > 0
484
- }
485
-
486
- // New model pointer system methods
487
-
488
- /**
489
- * Get model by pointer type (main, task, reasoning, quick)
490
- */
491
- getModel(pointer: ModelPointerType): ModelProfile | null {
492
- const pointerId = this.config.modelPointers?.[pointer]
493
- if (!pointerId) {
494
- return this.getDefaultModel()
495
- }
496
-
497
- const profile = this.findModelProfile(pointerId)
498
- return profile && profile.isActive ? profile : this.getDefaultModel()
499
- }
500
-
501
- /**
502
- * Get model name by pointer type
503
- */
504
- getModelName(pointer: ModelPointerType): string | null {
505
- const profile = this.getModel(pointer)
506
- return profile ? profile.modelName : null
507
- }
508
-
509
- /**
510
- * Get reasoning model (with fallback)
511
- */
512
- getReasoningModel(): string | null {
513
- return this.getModelName('reasoning') || this.getModelName('main')
514
- }
515
-
516
- /**
517
- * Get quick model (with fallback)
518
- */
519
- getQuickModel(): string | null {
520
- return (
521
- this.getModelName('quick') ||
522
- this.getModelName('task') ||
523
- this.getModelName('main')
524
- )
525
- }
526
-
527
- /**
528
- * Add a new model profile with duplicate validation
529
- */
530
- async addModel(
531
- config: Omit<ModelProfile, 'createdAt' | 'isActive'>,
532
- ): Promise<string> {
533
- // Check for duplicate modelName (actual model identifier)
534
- const existingByModelName = this.modelProfiles.find(
535
- p => p.modelName === config.modelName,
536
- )
537
- if (existingByModelName) {
538
- throw new Error(
539
- `Model with modelName '${config.modelName}' already exists: ${existingByModelName.name}`,
540
- )
541
- }
542
-
543
- // Check for duplicate friendly name
544
- const existingByName = this.modelProfiles.find(p => p.name === config.name)
545
- if (existingByName) {
546
- throw new Error(`Model with name '${config.name}' already exists`)
547
- }
548
-
549
- const newModel: ModelProfile = {
550
- ...config,
551
- createdAt: Date.now(),
552
- isActive: true,
553
- }
554
-
555
- this.modelProfiles.push(newModel)
556
-
557
- // If this is the first model, set all pointers to it
558
- if (this.modelProfiles.length === 1) {
559
- this.config.modelPointers = {
560
- main: config.modelName,
561
- task: config.modelName,
562
- reasoning: config.modelName,
563
- quick: config.modelName,
564
- }
565
- this.config.defaultModelName = config.modelName
566
- }
567
-
568
- this.saveConfig()
569
- return config.modelName
570
- }
571
-
572
- /**
573
- * Set model pointer assignment
574
- */
575
- setPointer(pointer: ModelPointerType, modelName: string): void {
576
- if (!this.findModelProfile(modelName)) {
577
- throw new Error(`Model '${modelName}' not found`)
578
- }
579
-
580
- if (!this.config.modelPointers) {
581
- this.config.modelPointers = {
582
- main: '',
583
- task: '',
584
- reasoning: '',
585
- quick: '',
586
- }
587
- }
588
-
589
- this.config.modelPointers[pointer] = modelName
590
- this.saveConfig()
591
- }
592
-
593
- /**
594
- * Get all active models for pointer assignment
595
- */
596
- getAvailableModels(): ModelProfile[] {
597
- return this.modelProfiles.filter(p => p.isActive)
598
- }
599
-
600
- /**
601
- * Get all configured models (both active and inactive) for switching
602
- */
603
- getAllConfiguredModels(): ModelProfile[] {
604
- return this.modelProfiles
605
- }
606
-
607
- /**
608
- * Get all available model names (modelName field) - active only
609
- */
610
- getAllAvailableModelNames(): string[] {
611
- return this.getAvailableModels().map(p => p.modelName)
612
- }
613
-
614
- /**
615
- * Get all configured model names (both active and inactive)
616
- */
617
- getAllConfiguredModelNames(): string[] {
618
- return this.getAllConfiguredModels().map(p => p.modelName)
619
- }
620
-
621
- /**
622
- * Debug method to get detailed model switching information
623
- */
624
- getModelSwitchingDebugInfo(): {
625
- totalModels: number
626
- activeModels: number
627
- inactiveModels: number
628
- currentMainModel: string | null
629
- availableModels: Array<{
630
- name: string
631
- modelName: string
632
- provider: string
633
- isActive: boolean
634
- lastUsed?: number
635
- }>
636
- modelPointers: Record<string, string | undefined>
637
- } {
638
- const availableModels = this.getAvailableModels()
639
- const currentMainModelName = this.config.modelPointers?.main
640
-
641
- return {
642
- totalModels: this.modelProfiles.length,
643
- activeModels: availableModels.length,
644
- inactiveModels: this.modelProfiles.length - availableModels.length,
645
- currentMainModel: currentMainModelName || null,
646
- availableModels: this.modelProfiles.map(p => ({
647
- name: p.name,
648
- modelName: p.modelName,
649
- provider: p.provider,
650
- isActive: p.isActive,
651
- lastUsed: p.lastUsed,
652
- })),
653
- modelPointers: this.config.modelPointers || {},
654
- }
655
- }
656
-
657
- /**
658
- * Remove a model profile
659
- */
660
- removeModel(modelName: string): void {
661
- this.modelProfiles = this.modelProfiles.filter(
662
- p => p.modelName !== modelName,
663
- )
664
-
665
- // Clean up pointers that reference deleted model
666
- if (this.config.modelPointers) {
667
- Object.keys(this.config.modelPointers).forEach(pointer => {
668
- if (
669
- this.config.modelPointers[pointer as ModelPointerType] === modelName
670
- ) {
671
- this.config.modelPointers[pointer as ModelPointerType] =
672
- this.config.defaultModelName || ''
673
- }
674
- })
675
- }
676
-
677
- this.saveConfig()
678
- }
679
-
680
- /**
681
- * Get default model profile
682
- */
683
- private getDefaultModel(): ModelProfile | null {
684
- if (this.config.defaultModelId) {
685
- const profile = this.findModelProfile(this.config.defaultModelId)
686
- if (profile && profile.isActive) {
687
- return profile
688
- }
689
- }
690
- return this.modelProfiles.find(p => p.isActive) || null
691
- }
692
-
693
- /**
694
- * Save configuration changes
695
- */
696
- private saveConfig(): void {
697
- const updatedConfig = {
698
- ...this.config,
699
- modelProfiles: this.modelProfiles,
700
- }
701
- saveGlobalConfig(updatedConfig)
702
- }
703
-
704
- /**
705
- * Get a fallback model when no specific model is configured
706
- */
707
- async getFallbackModel(): Promise<string> {
708
- const modelConfig = await getModelConfig()
709
- if (USE_BEDROCK) return modelConfig.bedrock
710
- if (USE_VERTEX) return modelConfig.vertex
711
- return modelConfig.firstParty
712
- }
713
-
714
- /**
715
- * 统一的模型解析方法:支持指针、model ID 和真实模型名称
716
- * @param modelParam - 可以是模型指针 ('main', 'task', etc.)、内部model ID 或真实模型名称 ('gpt-4o', 'claude-3-5-sonnet')
717
- * @returns ModelProfile 或 null
718
- */
719
- resolveModel(modelParam: string | ModelPointerType): ModelProfile | null {
720
- // 首先检查是否是模型指针
721
- if (['main', 'task', 'reasoning', 'quick'].includes(modelParam)) {
722
- const pointerId =
723
- this.config.modelPointers?.[modelParam as ModelPointerType]
724
- if (pointerId) {
725
- // pointerId 可能是内部ID或真实模型名称,尝试两种查找方式
726
- let profile = this.findModelProfile(pointerId) // 按内部ID查找
727
- if (!profile) {
728
- profile = this.findModelProfileByModelName(pointerId) // 按真实模型名查找
729
- }
730
- if (profile && profile.isActive) {
731
- return profile
732
- }
733
- }
734
- // 指针无效时,尝试 fallback 到默认模型
735
- return this.getDefaultModel()
736
- }
737
-
738
- // 不是指针,尝试多种查找方式
739
- // 1. 尝试按内部 model ID 查找
740
- let profile = this.findModelProfile(modelParam)
741
- if (profile && profile.isActive) {
742
- return profile
743
- }
744
-
745
- // 2. 尝试按真实模型名称查找
746
- profile = this.findModelProfileByModelName(modelParam)
747
- if (profile && profile.isActive) {
748
- return profile
749
- }
750
-
751
- // 3. 尝试按友好名称查找
752
- profile = this.findModelProfileByName(modelParam)
753
- if (profile && profile.isActive) {
754
- return profile
755
- }
756
-
757
- // 所有查找方式都失败,尝试 fallback 到默认模型
758
- return this.getDefaultModel()
759
- }
760
-
761
- /**
762
- * 解析模型参数并返回完整信息
763
- */
764
- resolveModelWithInfo(modelParam: string | ModelPointerType): {
765
- success: boolean
766
- profile: ModelProfile | null
767
- error?: string
768
- } {
769
- const isPointer = ['main', 'task', 'reasoning', 'quick'].includes(
770
- modelParam,
771
- )
772
-
773
- if (isPointer) {
774
- const pointerId =
775
- this.config.modelPointers?.[modelParam as ModelPointerType]
776
- if (!pointerId) {
777
- return {
778
- success: false,
779
- profile: null,
780
- error: `Model pointer '${modelParam}' is not configured. Use /model to set up models.`,
781
- }
782
- }
783
-
784
- // pointerId 可能是内部ID或真实模型名称
785
- let profile = this.findModelProfile(pointerId)
786
- if (!profile) {
787
- profile = this.findModelProfileByModelName(pointerId)
788
- }
789
-
790
- if (!profile) {
791
- return {
792
- success: false,
793
- profile: null,
794
- error: `Model pointer '${modelParam}' points to invalid model '${pointerId}'. Use /model to reconfigure.`,
795
- }
796
- }
797
-
798
- if (!profile.isActive) {
799
- return {
800
- success: false,
801
- profile: null,
802
- error: `Model '${profile.name}' (pointed by '${modelParam}') is inactive. Use /model to activate it.`,
803
- }
804
- }
805
-
806
- return {
807
- success: true,
808
- profile,
809
- }
810
- } else {
811
- // 直接的 model ID 或模型名称,尝试多种查找方式
812
- let profile = this.findModelProfile(modelParam)
813
- if (!profile) {
814
- profile = this.findModelProfileByModelName(modelParam)
815
- }
816
- if (!profile) {
817
- profile = this.findModelProfileByName(modelParam)
818
- }
819
-
820
- if (!profile) {
821
- return {
822
- success: false,
823
- profile: null,
824
- error: `Model '${modelParam}' not found. Use /model to add models.`,
825
- }
826
- }
827
-
828
- if (!profile.isActive) {
829
- return {
830
- success: false,
831
- profile: null,
832
- error: `Model '${profile.name}' is inactive. Use /model to activate it.`,
833
- }
834
- }
835
-
836
- return {
837
- success: true,
838
- profile,
839
- }
840
- }
841
- }
842
-
843
- // Private helper methods
844
- private findModelProfile(modelName: string): ModelProfile | null {
845
- return this.modelProfiles.find(p => p.modelName === modelName) || null
846
- }
847
-
848
- private findModelProfileByModelName(modelName: string): ModelProfile | null {
849
- return this.modelProfiles.find(p => p.modelName === modelName) || null
850
- }
851
-
852
- private findModelProfileByName(name: string): ModelProfile | null {
853
- return this.modelProfiles.find(p => p.name === name) || null
854
- }
855
-
856
- private updateLastUsed(modelName: string): void {
857
- const profile = this.findModelProfile(modelName)
858
- if (profile) {
859
- profile.lastUsed = Date.now()
860
- }
861
- }
862
- }
863
-
864
- // Global ModelManager instance to avoid config read/write race conditions
865
- let globalModelManager: ModelManager | null = null
866
-
867
- /**
868
- * Get the global ModelManager instance (singleton pattern to fix race conditions)
869
- */
870
- export const getModelManager = (): ModelManager => {
871
- try {
872
- if (!globalModelManager) {
873
- const config = getGlobalConfig()
874
- if (!config) {
875
- console.warn(
876
- 'No global config available, creating ModelManager with empty config',
877
- )
878
- globalModelManager = new ModelManager({
879
- modelProfiles: [],
880
- modelPointers: { main: '', task: '', reasoning: '', quick: '' },
881
- })
882
- } else {
883
- globalModelManager = new ModelManager(config)
884
- }
885
- }
886
- return globalModelManager
887
- } catch (error) {
888
- console.error('Error creating ModelManager:', error)
889
- // Return a fallback ModelManager with empty configuration
890
- return new ModelManager({
891
- modelProfiles: [],
892
- modelPointers: { main: '', task: '', reasoning: '', quick: '' },
893
- })
894
- }
895
- }
896
-
897
- /**
898
- * Force reload of the global ModelManager instance
899
- * Used when configuration changes to ensure fresh data
900
- */
901
- export const reloadModelManager = (): void => {
902
- globalModelManager = null
903
- // Force creation of new instance with fresh config
904
- getModelManager()
905
- }
906
-
907
- /**
908
- * Get the quick model for fast operations
909
- */
910
- export const getQuickModel = (): string => {
911
- const manager = getModelManager()
912
- const quickModel = manager.getModel('quick')
913
- return quickModel?.modelName || 'quick' // Return pointer if model not resolved
914
- }