@shareai-lab/kode 1.1.13 → 1.1.16-dev.1

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 (288) hide show
  1. package/dist/entrypoints/cli.js +59 -38
  2. package/dist/entrypoints/cli.js.map +3 -3
  3. package/dist/index.js +5 -26
  4. package/dist/package.json +4 -1
  5. package/package.json +11 -104
  6. package/dist/test/testAdapters.js +0 -88
  7. package/dist/test/testAdapters.js.map +0 -1
  8. package/src/ProjectOnboarding.tsx +0 -198
  9. package/src/Tool.ts +0 -83
  10. package/src/commands/agents.tsx +0 -3416
  11. package/src/commands/approvedTools.ts +0 -53
  12. package/src/commands/bug.tsx +0 -20
  13. package/src/commands/clear.ts +0 -43
  14. package/src/commands/compact.ts +0 -120
  15. package/src/commands/config.tsx +0 -19
  16. package/src/commands/cost.ts +0 -18
  17. package/src/commands/ctx_viz.ts +0 -209
  18. package/src/commands/doctor.ts +0 -24
  19. package/src/commands/help.tsx +0 -19
  20. package/src/commands/init.ts +0 -37
  21. package/src/commands/listen.ts +0 -42
  22. package/src/commands/login.tsx +0 -51
  23. package/src/commands/logout.tsx +0 -40
  24. package/src/commands/mcp.ts +0 -41
  25. package/src/commands/model.tsx +0 -40
  26. package/src/commands/modelstatus.tsx +0 -20
  27. package/src/commands/onboarding.tsx +0 -34
  28. package/src/commands/pr_comments.ts +0 -59
  29. package/src/commands/refreshCommands.ts +0 -54
  30. package/src/commands/release-notes.ts +0 -34
  31. package/src/commands/resume.tsx +0 -31
  32. package/src/commands/review.ts +0 -49
  33. package/src/commands/terminalSetup.ts +0 -221
  34. package/src/commands.ts +0 -139
  35. package/src/components/ApproveApiKey.tsx +0 -93
  36. package/src/components/AsciiLogo.tsx +0 -13
  37. package/src/components/AutoUpdater.tsx +0 -148
  38. package/src/components/Bug.tsx +0 -367
  39. package/src/components/Config.tsx +0 -293
  40. package/src/components/ConsoleOAuthFlow.tsx +0 -327
  41. package/src/components/Cost.tsx +0 -23
  42. package/src/components/CostThresholdDialog.tsx +0 -46
  43. package/src/components/CustomSelect/option-map.ts +0 -42
  44. package/src/components/CustomSelect/select-option.tsx +0 -78
  45. package/src/components/CustomSelect/select.tsx +0 -152
  46. package/src/components/CustomSelect/theme.ts +0 -45
  47. package/src/components/CustomSelect/use-select-state.ts +0 -414
  48. package/src/components/CustomSelect/use-select.ts +0 -35
  49. package/src/components/FallbackToolUseRejectedMessage.tsx +0 -15
  50. package/src/components/FileEditToolUpdatedMessage.tsx +0 -66
  51. package/src/components/Help.tsx +0 -215
  52. package/src/components/HighlightedCode.tsx +0 -33
  53. package/src/components/InvalidConfigDialog.tsx +0 -113
  54. package/src/components/Link.tsx +0 -32
  55. package/src/components/LogSelector.tsx +0 -86
  56. package/src/components/Logo.tsx +0 -170
  57. package/src/components/MCPServerApprovalDialog.tsx +0 -100
  58. package/src/components/MCPServerDialogCopy.tsx +0 -25
  59. package/src/components/MCPServerMultiselectDialog.tsx +0 -109
  60. package/src/components/Message.tsx +0 -221
  61. package/src/components/MessageResponse.tsx +0 -15
  62. package/src/components/MessageSelector.tsx +0 -211
  63. package/src/components/ModeIndicator.tsx +0 -88
  64. package/src/components/ModelConfig.tsx +0 -301
  65. package/src/components/ModelListManager.tsx +0 -227
  66. package/src/components/ModelSelector.tsx +0 -3387
  67. package/src/components/ModelStatusDisplay.tsx +0 -230
  68. package/src/components/Onboarding.tsx +0 -274
  69. package/src/components/PressEnterToContinue.tsx +0 -11
  70. package/src/components/PromptInput.tsx +0 -760
  71. package/src/components/SentryErrorBoundary.ts +0 -39
  72. package/src/components/Spinner.tsx +0 -129
  73. package/src/components/StickerRequestForm.tsx +0 -16
  74. package/src/components/StructuredDiff.tsx +0 -191
  75. package/src/components/TextInput.tsx +0 -259
  76. package/src/components/TodoItem.tsx +0 -47
  77. package/src/components/TokenWarning.tsx +0 -31
  78. package/src/components/ToolUseLoader.tsx +0 -40
  79. package/src/components/TrustDialog.tsx +0 -106
  80. package/src/components/binary-feedback/BinaryFeedback.tsx +0 -63
  81. package/src/components/binary-feedback/BinaryFeedbackOption.tsx +0 -111
  82. package/src/components/binary-feedback/BinaryFeedbackView.tsx +0 -172
  83. package/src/components/binary-feedback/utils.ts +0 -220
  84. package/src/components/messages/AssistantBashOutputMessage.tsx +0 -22
  85. package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +0 -49
  86. package/src/components/messages/AssistantRedactedThinkingMessage.tsx +0 -19
  87. package/src/components/messages/AssistantTextMessage.tsx +0 -144
  88. package/src/components/messages/AssistantThinkingMessage.tsx +0 -40
  89. package/src/components/messages/AssistantToolUseMessage.tsx +0 -132
  90. package/src/components/messages/TaskProgressMessage.tsx +0 -32
  91. package/src/components/messages/TaskToolMessage.tsx +0 -58
  92. package/src/components/messages/UserBashInputMessage.tsx +0 -28
  93. package/src/components/messages/UserCommandMessage.tsx +0 -30
  94. package/src/components/messages/UserKodingInputMessage.tsx +0 -28
  95. package/src/components/messages/UserPromptMessage.tsx +0 -35
  96. package/src/components/messages/UserTextMessage.tsx +0 -39
  97. package/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx +0 -12
  98. package/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +0 -36
  99. package/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +0 -31
  100. package/src/components/messages/UserToolResultMessage/UserToolResultMessage.tsx +0 -57
  101. package/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +0 -35
  102. package/src/components/messages/UserToolResultMessage/utils.tsx +0 -56
  103. package/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +0 -121
  104. package/src/components/permissions/FallbackPermissionRequest.tsx +0 -153
  105. package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +0 -182
  106. package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +0 -77
  107. package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +0 -164
  108. package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +0 -83
  109. package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +0 -240
  110. package/src/components/permissions/PermissionRequest.tsx +0 -101
  111. package/src/components/permissions/PermissionRequestTitle.tsx +0 -69
  112. package/src/components/permissions/hooks.ts +0 -44
  113. package/src/components/permissions/toolUseOptions.ts +0 -59
  114. package/src/components/permissions/utils.ts +0 -23
  115. package/src/constants/betas.ts +0 -5
  116. package/src/constants/claude-asterisk-ascii-art.tsx +0 -238
  117. package/src/constants/figures.ts +0 -4
  118. package/src/constants/keys.ts +0 -3
  119. package/src/constants/macros.ts +0 -11
  120. package/src/constants/modelCapabilities.ts +0 -179
  121. package/src/constants/models.ts +0 -1025
  122. package/src/constants/oauth.ts +0 -18
  123. package/src/constants/product.ts +0 -17
  124. package/src/constants/prompts.ts +0 -168
  125. package/src/constants/releaseNotes.ts +0 -7
  126. package/src/context/PermissionContext.tsx +0 -149
  127. package/src/context.ts +0 -278
  128. package/src/cost-tracker.ts +0 -84
  129. package/src/entrypoints/cli.tsx +0 -1561
  130. package/src/entrypoints/mcp.ts +0 -175
  131. package/src/history.ts +0 -25
  132. package/src/hooks/useApiKeyVerification.ts +0 -59
  133. package/src/hooks/useArrowKeyHistory.ts +0 -55
  134. package/src/hooks/useCanUseTool.ts +0 -138
  135. package/src/hooks/useCancelRequest.ts +0 -39
  136. package/src/hooks/useDoublePress.ts +0 -41
  137. package/src/hooks/useExitOnCtrlCD.ts +0 -31
  138. package/src/hooks/useInterval.ts +0 -25
  139. package/src/hooks/useLogMessages.ts +0 -16
  140. package/src/hooks/useLogStartupTime.ts +0 -12
  141. package/src/hooks/useNotifyAfterTimeout.ts +0 -65
  142. package/src/hooks/usePermissionRequestLogging.ts +0 -44
  143. package/src/hooks/useTerminalSize.ts +0 -49
  144. package/src/hooks/useTextInput.ts +0 -317
  145. package/src/hooks/useUnifiedCompletion.ts +0 -1405
  146. package/src/index.ts +0 -34
  147. package/src/messages.ts +0 -38
  148. package/src/permissions.ts +0 -268
  149. package/src/query.ts +0 -720
  150. package/src/screens/ConfigureNpmPrefix.tsx +0 -197
  151. package/src/screens/Doctor.tsx +0 -219
  152. package/src/screens/LogList.tsx +0 -68
  153. package/src/screens/REPL.tsx +0 -813
  154. package/src/screens/ResumeConversation.tsx +0 -68
  155. package/src/services/adapters/base.ts +0 -38
  156. package/src/services/adapters/chatCompletions.ts +0 -90
  157. package/src/services/adapters/responsesAPI.ts +0 -170
  158. package/src/services/browserMocks.ts +0 -66
  159. package/src/services/claude.ts +0 -2197
  160. package/src/services/customCommands.ts +0 -704
  161. package/src/services/fileFreshness.ts +0 -377
  162. package/src/services/gpt5ConnectionTest.ts +0 -340
  163. package/src/services/mcpClient.ts +0 -564
  164. package/src/services/mcpServerApproval.tsx +0 -50
  165. package/src/services/mentionProcessor.ts +0 -273
  166. package/src/services/modelAdapterFactory.ts +0 -69
  167. package/src/services/notifier.ts +0 -40
  168. package/src/services/oauth.ts +0 -357
  169. package/src/services/openai.ts +0 -1359
  170. package/src/services/responseStateManager.ts +0 -90
  171. package/src/services/sentry.ts +0 -3
  172. package/src/services/statsig.ts +0 -172
  173. package/src/services/statsigStorage.ts +0 -86
  174. package/src/services/systemReminder.ts +0 -507
  175. package/src/services/vcr.ts +0 -161
  176. package/src/test/testAdapters.ts +0 -96
  177. package/src/tools/ArchitectTool/ArchitectTool.tsx +0 -135
  178. package/src/tools/ArchitectTool/prompt.ts +0 -15
  179. package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +0 -576
  180. package/src/tools/BashTool/BashTool.tsx +0 -243
  181. package/src/tools/BashTool/BashToolResultMessage.tsx +0 -38
  182. package/src/tools/BashTool/OutputLine.tsx +0 -49
  183. package/src/tools/BashTool/prompt.ts +0 -174
  184. package/src/tools/BashTool/utils.ts +0 -56
  185. package/src/tools/FileEditTool/FileEditTool.tsx +0 -319
  186. package/src/tools/FileEditTool/prompt.ts +0 -51
  187. package/src/tools/FileEditTool/utils.ts +0 -58
  188. package/src/tools/FileReadTool/FileReadTool.tsx +0 -404
  189. package/src/tools/FileReadTool/prompt.ts +0 -7
  190. package/src/tools/FileWriteTool/FileWriteTool.tsx +0 -301
  191. package/src/tools/FileWriteTool/prompt.ts +0 -10
  192. package/src/tools/GlobTool/GlobTool.tsx +0 -119
  193. package/src/tools/GlobTool/prompt.ts +0 -8
  194. package/src/tools/GrepTool/GrepTool.tsx +0 -147
  195. package/src/tools/GrepTool/prompt.ts +0 -11
  196. package/src/tools/MCPTool/MCPTool.tsx +0 -107
  197. package/src/tools/MCPTool/prompt.ts +0 -3
  198. package/src/tools/MemoryReadTool/MemoryReadTool.tsx +0 -127
  199. package/src/tools/MemoryReadTool/prompt.ts +0 -3
  200. package/src/tools/MemoryWriteTool/MemoryWriteTool.tsx +0 -89
  201. package/src/tools/MemoryWriteTool/prompt.ts +0 -3
  202. package/src/tools/MultiEditTool/MultiEditTool.tsx +0 -388
  203. package/src/tools/MultiEditTool/prompt.ts +0 -45
  204. package/src/tools/NotebookEditTool/NotebookEditTool.tsx +0 -298
  205. package/src/tools/NotebookEditTool/prompt.ts +0 -3
  206. package/src/tools/NotebookReadTool/NotebookReadTool.tsx +0 -258
  207. package/src/tools/NotebookReadTool/prompt.ts +0 -3
  208. package/src/tools/StickerRequestTool/StickerRequestTool.tsx +0 -107
  209. package/src/tools/StickerRequestTool/prompt.ts +0 -19
  210. package/src/tools/TaskTool/TaskTool.tsx +0 -438
  211. package/src/tools/TaskTool/constants.ts +0 -1
  212. package/src/tools/TaskTool/prompt.ts +0 -92
  213. package/src/tools/ThinkTool/ThinkTool.tsx +0 -54
  214. package/src/tools/ThinkTool/prompt.ts +0 -12
  215. package/src/tools/TodoWriteTool/TodoWriteTool.tsx +0 -313
  216. package/src/tools/TodoWriteTool/prompt.ts +0 -63
  217. package/src/tools/URLFetcherTool/URLFetcherTool.tsx +0 -178
  218. package/src/tools/URLFetcherTool/cache.ts +0 -55
  219. package/src/tools/URLFetcherTool/htmlToMarkdown.ts +0 -55
  220. package/src/tools/URLFetcherTool/prompt.ts +0 -17
  221. package/src/tools/WebSearchTool/WebSearchTool.tsx +0 -103
  222. package/src/tools/WebSearchTool/prompt.ts +0 -13
  223. package/src/tools/WebSearchTool/searchProviders.ts +0 -66
  224. package/src/tools/lsTool/lsTool.tsx +0 -272
  225. package/src/tools/lsTool/prompt.ts +0 -2
  226. package/src/tools.ts +0 -67
  227. package/src/types/PermissionMode.ts +0 -120
  228. package/src/types/RequestContext.ts +0 -72
  229. package/src/types/common.d.ts +0 -2
  230. package/src/types/conversation.ts +0 -51
  231. package/src/types/logs.ts +0 -58
  232. package/src/types/modelCapabilities.ts +0 -64
  233. package/src/types/notebook.ts +0 -87
  234. package/src/utils/Cursor.ts +0 -436
  235. package/src/utils/PersistentShell.ts +0 -552
  236. package/src/utils/advancedFuzzyMatcher.ts +0 -290
  237. package/src/utils/agentLoader.ts +0 -278
  238. package/src/utils/agentStorage.ts +0 -97
  239. package/src/utils/array.ts +0 -3
  240. package/src/utils/ask.tsx +0 -99
  241. package/src/utils/auth.ts +0 -13
  242. package/src/utils/autoCompactCore.ts +0 -223
  243. package/src/utils/autoUpdater.ts +0 -458
  244. package/src/utils/betas.ts +0 -20
  245. package/src/utils/browser.ts +0 -14
  246. package/src/utils/cleanup.ts +0 -72
  247. package/src/utils/commands.ts +0 -261
  248. package/src/utils/commonUnixCommands.ts +0 -161
  249. package/src/utils/config.ts +0 -945
  250. package/src/utils/conversationRecovery.ts +0 -55
  251. package/src/utils/debugLogger.ts +0 -1235
  252. package/src/utils/diff.ts +0 -42
  253. package/src/utils/env.ts +0 -57
  254. package/src/utils/errors.ts +0 -21
  255. package/src/utils/exampleCommands.ts +0 -109
  256. package/src/utils/execFileNoThrow.ts +0 -51
  257. package/src/utils/expertChatStorage.ts +0 -136
  258. package/src/utils/file.ts +0 -405
  259. package/src/utils/fileRecoveryCore.ts +0 -71
  260. package/src/utils/format.tsx +0 -44
  261. package/src/utils/fuzzyMatcher.ts +0 -328
  262. package/src/utils/generators.ts +0 -62
  263. package/src/utils/git.ts +0 -92
  264. package/src/utils/globalLogger.ts +0 -77
  265. package/src/utils/http.ts +0 -10
  266. package/src/utils/imagePaste.ts +0 -38
  267. package/src/utils/json.ts +0 -13
  268. package/src/utils/log.ts +0 -382
  269. package/src/utils/markdown.ts +0 -213
  270. package/src/utils/messageContextManager.ts +0 -294
  271. package/src/utils/messages.tsx +0 -945
  272. package/src/utils/model.ts +0 -914
  273. package/src/utils/permissions/filesystem.ts +0 -127
  274. package/src/utils/responseState.ts +0 -23
  275. package/src/utils/ripgrep.ts +0 -167
  276. package/src/utils/secureFile.ts +0 -564
  277. package/src/utils/sessionState.ts +0 -49
  278. package/src/utils/state.ts +0 -25
  279. package/src/utils/style.ts +0 -29
  280. package/src/utils/terminal.ts +0 -50
  281. package/src/utils/theme.ts +0 -127
  282. package/src/utils/thinking.ts +0 -144
  283. package/src/utils/todoStorage.ts +0 -431
  284. package/src/utils/tokens.ts +0 -43
  285. package/src/utils/toolExecutionController.ts +0 -163
  286. package/src/utils/unaryLogging.ts +0 -26
  287. package/src/utils/user.ts +0 -37
  288. 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
- }