@vybestack/llxprt-code 0.4.8 → 0.5.0-nightly.251102.6bb3db7a

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 (269) hide show
  1. package/dist/package.json +5 -3
  2. package/dist/src/auth/__tests__/oauthManager.safety.test.d.ts +6 -0
  3. package/dist/src/auth/__tests__/oauthManager.safety.test.js +49 -0
  4. package/dist/src/auth/__tests__/oauthManager.safety.test.js.map +1 -0
  5. package/dist/src/auth/oauth-manager.d.ts +11 -0
  6. package/dist/src/auth/oauth-manager.js +62 -29
  7. package/dist/src/auth/oauth-manager.js.map +1 -1
  8. package/dist/src/auth/oauth-manager.spec.js +7 -2
  9. package/dist/src/auth/oauth-manager.spec.js.map +1 -1
  10. package/dist/src/config/__tests__/nonInteractiveTools.test.d.ts +6 -0
  11. package/dist/src/config/__tests__/nonInteractiveTools.test.js +13 -0
  12. package/dist/src/config/__tests__/nonInteractiveTools.test.js.map +1 -0
  13. package/dist/src/config/__tests__/profileBootstrap.test.d.ts +6 -0
  14. package/dist/src/config/__tests__/profileBootstrap.test.js +91 -0
  15. package/dist/src/config/__tests__/profileBootstrap.test.js.map +1 -0
  16. package/dist/src/config/config.d.ts +6 -2
  17. package/dist/src/config/config.js +219 -18
  18. package/dist/src/config/config.js.map +1 -1
  19. package/dist/src/config/profileBootstrap.d.ts +64 -0
  20. package/dist/src/config/profileBootstrap.js +140 -0
  21. package/dist/src/config/profileBootstrap.js.map +1 -0
  22. package/dist/src/gemini.js +68 -23
  23. package/dist/src/gemini.js.map +1 -1
  24. package/dist/src/gemini.test.js +1 -2
  25. package/dist/src/gemini.test.js.map +1 -1
  26. package/dist/src/generated/git-commit.d.ts +1 -1
  27. package/dist/src/generated/git-commit.js +1 -1
  28. package/dist/src/integration-tests/base-url-behavior.integration.test.js +110 -450
  29. package/dist/src/integration-tests/base-url-behavior.integration.test.js.map +1 -1
  30. package/dist/src/integration-tests/model-params-isolation.integration.test.js +101 -539
  31. package/dist/src/integration-tests/model-params-isolation.integration.test.js.map +1 -1
  32. package/dist/src/integration-tests/modelParams.integration.test.js +86 -761
  33. package/dist/src/integration-tests/modelParams.integration.test.js.map +1 -1
  34. package/dist/src/integration-tests/provider-multi-runtime.integration.test.d.ts +6 -0
  35. package/dist/src/integration-tests/provider-multi-runtime.integration.test.js +198 -0
  36. package/dist/src/integration-tests/provider-multi-runtime.integration.test.js.map +1 -0
  37. package/dist/src/integration-tests/provider-switching.integration.test.js +97 -151
  38. package/dist/src/integration-tests/provider-switching.integration.test.js.map +1 -1
  39. package/dist/src/integration-tests/runtime-isolation.test.d.ts +13 -0
  40. package/dist/src/integration-tests/runtime-isolation.test.js +170 -0
  41. package/dist/src/integration-tests/runtime-isolation.test.js.map +1 -0
  42. package/dist/src/integration-tests/test-utils.js +19 -2
  43. package/dist/src/integration-tests/test-utils.js.map +1 -1
  44. package/dist/src/integration-tests/test-utils.test.js +9 -8
  45. package/dist/src/integration-tests/test-utils.test.js.map +1 -1
  46. package/dist/src/integration-tests/todo-continuation.integration.test.js +5 -2
  47. package/dist/src/integration-tests/todo-continuation.integration.test.js.map +1 -1
  48. package/dist/src/integration-tests/tools-governance.integration.test.d.ts +6 -0
  49. package/dist/src/integration-tests/tools-governance.integration.test.js +98 -0
  50. package/dist/src/integration-tests/tools-governance.integration.test.js.map +1 -0
  51. package/dist/src/nonInteractiveCli.js +36 -11
  52. package/dist/src/nonInteractiveCli.js.map +1 -1
  53. package/dist/src/providers/logging/git-stats.test.js +11 -1
  54. package/dist/src/providers/logging/git-stats.test.js.map +1 -1
  55. package/dist/src/providers/logging/multi-provider-logging.integration.test.js +1 -2
  56. package/dist/src/providers/logging/multi-provider-logging.integration.test.js.map +1 -1
  57. package/dist/src/providers/logging/performance.test.js +1 -1
  58. package/dist/src/providers/logging/performance.test.js.map +1 -1
  59. package/dist/src/providers/oauth-provider-registration.d.ts +2 -2
  60. package/dist/src/providers/oauth-provider-registration.js +25 -9
  61. package/dist/src/providers/oauth-provider-registration.js.map +1 -1
  62. package/dist/src/providers/provider-gemini-switching.test.js +67 -89
  63. package/dist/src/providers/provider-gemini-switching.test.js.map +1 -1
  64. package/dist/src/providers/provider-switching.integration.test.js +42 -98
  65. package/dist/src/providers/provider-switching.integration.test.js.map +1 -1
  66. package/dist/src/providers/providerConfigUtils.d.ts +12 -7
  67. package/dist/src/providers/providerConfigUtils.js +31 -99
  68. package/dist/src/providers/providerConfigUtils.js.map +1 -1
  69. package/dist/src/providers/providerManagerInstance.d.ts +17 -1
  70. package/dist/src/providers/providerManagerInstance.js +157 -175
  71. package/dist/src/providers/providerManagerInstance.js.map +1 -1
  72. package/dist/src/providers/providerManagerInstance.oauthRegistration.test.js +19 -15
  73. package/dist/src/providers/providerManagerInstance.oauthRegistration.test.js.map +1 -1
  74. package/dist/src/providers/providerManagerInstance.test.js +2 -5
  75. package/dist/src/providers/providerManagerInstance.test.js.map +1 -1
  76. package/dist/src/runtime/__tests__/profileApplication.test.d.ts +5 -0
  77. package/dist/src/runtime/__tests__/profileApplication.test.js +232 -0
  78. package/dist/src/runtime/__tests__/profileApplication.test.js.map +1 -0
  79. package/dist/src/runtime/__tests__/runtimeIsolation.test.d.ts +5 -0
  80. package/dist/src/runtime/__tests__/runtimeIsolation.test.js +376 -0
  81. package/dist/src/runtime/__tests__/runtimeIsolation.test.js.map +1 -0
  82. package/dist/src/runtime/agentRuntimeAdapter.d.ts +249 -0
  83. package/dist/src/runtime/agentRuntimeAdapter.js +506 -0
  84. package/dist/src/runtime/agentRuntimeAdapter.js.map +1 -0
  85. package/dist/src/runtime/agentRuntimeAdapter.spec.d.ts +6 -0
  86. package/dist/src/runtime/agentRuntimeAdapter.spec.js +866 -0
  87. package/dist/src/runtime/agentRuntimeAdapter.spec.js.map +1 -0
  88. package/dist/src/runtime/messages.d.ts +28 -0
  89. package/dist/src/runtime/messages.js +64 -0
  90. package/dist/src/runtime/messages.js.map +1 -0
  91. package/dist/src/runtime/profileApplication.d.ts +33 -0
  92. package/dist/src/runtime/profileApplication.js +191 -0
  93. package/dist/src/runtime/profileApplication.js.map +1 -0
  94. package/dist/src/runtime/providerConfigUtils.test.d.ts +1 -0
  95. package/dist/src/runtime/providerConfigUtils.test.js +68 -0
  96. package/dist/src/runtime/providerConfigUtils.test.js.map +1 -0
  97. package/dist/src/runtime/runtimeContextFactory.d.ts +102 -0
  98. package/dist/src/runtime/runtimeContextFactory.js +190 -0
  99. package/dist/src/runtime/runtimeContextFactory.js.map +1 -0
  100. package/dist/src/runtime/runtimeSettings.d.ts +217 -0
  101. package/dist/src/runtime/runtimeSettings.js +1094 -0
  102. package/dist/src/runtime/runtimeSettings.js.map +1 -0
  103. package/dist/src/runtime/runtimeSettings.test.d.ts +1 -0
  104. package/dist/src/runtime/runtimeSettings.test.js +320 -0
  105. package/dist/src/runtime/runtimeSettings.test.js.map +1 -0
  106. package/dist/src/services/BuiltinCommandLoader.d.ts +13 -4
  107. package/dist/src/services/BuiltinCommandLoader.js +17 -4
  108. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  109. package/dist/src/services/McpPromptLoader.js +34 -13
  110. package/dist/src/services/McpPromptLoader.js.map +1 -1
  111. package/dist/src/test-utils/mockCommandContext.js +5 -2
  112. package/dist/src/test-utils/mockCommandContext.js.map +1 -1
  113. package/dist/src/ui/App.js +29 -49
  114. package/dist/src/ui/App.js.map +1 -1
  115. package/dist/src/ui/commands/aboutCommand.js +59 -38
  116. package/dist/src/ui/commands/aboutCommand.js.map +1 -1
  117. package/dist/src/ui/commands/authCommand.js +7 -9
  118. package/dist/src/ui/commands/authCommand.js.map +1 -1
  119. package/dist/src/ui/commands/baseurlCommand.js +8 -44
  120. package/dist/src/ui/commands/baseurlCommand.js.map +1 -1
  121. package/dist/src/ui/commands/chatCommand.js +28 -12
  122. package/dist/src/ui/commands/chatCommand.js.map +1 -1
  123. package/dist/src/ui/commands/diagnosticsCommand.d.ts +0 -3
  124. package/dist/src/ui/commands/diagnosticsCommand.js +45 -191
  125. package/dist/src/ui/commands/diagnosticsCommand.js.map +1 -1
  126. package/dist/src/ui/commands/keyCommand.js +9 -58
  127. package/dist/src/ui/commands/keyCommand.js.map +1 -1
  128. package/dist/src/ui/commands/keyCommand.test.js +48 -102
  129. package/dist/src/ui/commands/keyCommand.test.js.map +1 -1
  130. package/dist/src/ui/commands/keyfileCommand.js +42 -93
  131. package/dist/src/ui/commands/keyfileCommand.js.map +1 -1
  132. package/dist/src/ui/commands/logoutCommand.js +2 -2
  133. package/dist/src/ui/commands/logoutCommand.js.map +1 -1
  134. package/dist/src/ui/commands/mcpCommand.js +29 -7
  135. package/dist/src/ui/commands/mcpCommand.js.map +1 -1
  136. package/dist/src/ui/commands/modelCommand.js +8 -59
  137. package/dist/src/ui/commands/modelCommand.js.map +1 -1
  138. package/dist/src/ui/commands/profileCommand.js +151 -267
  139. package/dist/src/ui/commands/profileCommand.js.map +1 -1
  140. package/dist/src/ui/commands/profileCommand.test.js +88 -344
  141. package/dist/src/ui/commands/profileCommand.test.js.map +1 -1
  142. package/dist/src/ui/commands/providerCommand.js +9 -3
  143. package/dist/src/ui/commands/providerCommand.js.map +1 -1
  144. package/dist/src/ui/commands/restoreCommand.js +38 -18
  145. package/dist/src/ui/commands/restoreCommand.js.map +1 -1
  146. package/dist/src/ui/commands/schema/argumentResolver.test.d.ts +6 -0
  147. package/dist/src/ui/commands/schema/argumentResolver.test.js +619 -0
  148. package/dist/src/ui/commands/schema/argumentResolver.test.js.map +1 -0
  149. package/dist/src/ui/commands/schema/index.d.ts +15 -0
  150. package/dist/src/ui/commands/schema/index.js +320 -0
  151. package/dist/src/ui/commands/schema/index.js.map +1 -0
  152. package/dist/src/ui/commands/schema/types.d.ts +61 -0
  153. package/dist/src/ui/commands/schema/types.js +12 -0
  154. package/dist/src/ui/commands/schema/types.js.map +1 -0
  155. package/dist/src/ui/commands/setCommand.js +641 -325
  156. package/dist/src/ui/commands/setCommand.js.map +1 -1
  157. package/dist/src/ui/commands/setCommand.test.js +92 -388
  158. package/dist/src/ui/commands/setCommand.test.js.map +1 -1
  159. package/dist/src/ui/commands/statusCommand.js +2 -2
  160. package/dist/src/ui/commands/statusCommand.js.map +1 -1
  161. package/dist/src/ui/commands/subagentCommand.d.ts +16 -0
  162. package/dist/src/ui/commands/subagentCommand.js +674 -0
  163. package/dist/src/ui/commands/subagentCommand.js.map +1 -0
  164. package/dist/src/ui/commands/test/setCommand.mutation.test.d.ts +6 -0
  165. package/dist/src/ui/commands/test/setCommand.mutation.test.js +132 -0
  166. package/dist/src/ui/commands/test/setCommand.mutation.test.js.map +1 -0
  167. package/dist/src/ui/commands/test/setCommand.phase09.test.d.ts +6 -0
  168. package/dist/src/ui/commands/test/setCommand.phase09.test.js +222 -0
  169. package/dist/src/ui/commands/test/setCommand.phase09.test.js.map +1 -0
  170. package/dist/src/ui/commands/test/subagentCommand.schema.test.d.ts +6 -0
  171. package/dist/src/ui/commands/test/subagentCommand.schema.test.js +125 -0
  172. package/dist/src/ui/commands/test/subagentCommand.schema.test.js.map +1 -0
  173. package/dist/src/ui/commands/test/subagentCommand.test.d.ts +1 -0
  174. package/dist/src/ui/commands/test/subagentCommand.test.js +598 -0
  175. package/dist/src/ui/commands/test/subagentCommand.test.js.map +1 -0
  176. package/dist/src/ui/commands/toolformatCommand.js +25 -98
  177. package/dist/src/ui/commands/toolformatCommand.js.map +1 -1
  178. package/dist/src/ui/commands/toolformatCommand.test.js +56 -102
  179. package/dist/src/ui/commands/toolformatCommand.test.js.map +1 -1
  180. package/dist/src/ui/commands/toolsCommand.js +187 -31
  181. package/dist/src/ui/commands/toolsCommand.js.map +1 -1
  182. package/dist/src/ui/commands/types.d.ts +11 -2
  183. package/dist/src/ui/commands/types.js.map +1 -1
  184. package/dist/src/ui/components/AuthDialog.js +16 -55
  185. package/dist/src/ui/components/AuthDialog.js.map +1 -1
  186. package/dist/src/ui/components/Footer.js +4 -5
  187. package/dist/src/ui/components/Footer.js.map +1 -1
  188. package/dist/src/ui/components/HistoryItemDisplay.js +1 -1
  189. package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
  190. package/dist/src/ui/components/InputPrompt.js +1 -1
  191. package/dist/src/ui/components/InputPrompt.js.map +1 -1
  192. package/dist/src/ui/components/StatsDisplay.js +6 -11
  193. package/dist/src/ui/components/StatsDisplay.js.map +1 -1
  194. package/dist/src/ui/components/SuggestionsDisplay.d.ts +13 -1
  195. package/dist/src/ui/components/SuggestionsDisplay.js +22 -3
  196. package/dist/src/ui/components/SuggestionsDisplay.js.map +1 -1
  197. package/dist/src/ui/components/messages/ToolGroupMessage.d.ts +1 -0
  198. package/dist/src/ui/components/messages/ToolGroupMessage.js +14 -14
  199. package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
  200. package/dist/src/ui/components/messages/ToolGroupMessage.test.js +1 -0
  201. package/dist/src/ui/components/messages/ToolGroupMessage.test.js.map +1 -1
  202. package/dist/src/ui/containers/SessionController.js +61 -117
  203. package/dist/src/ui/containers/SessionController.js.map +1 -1
  204. package/dist/src/ui/contexts/RuntimeContext.d.ts +61 -0
  205. package/dist/src/ui/contexts/RuntimeContext.js +118 -0
  206. package/dist/src/ui/contexts/RuntimeContext.js.map +1 -0
  207. package/dist/src/ui/contexts/TodoProvider.d.ts +1 -0
  208. package/dist/src/ui/contexts/TodoProvider.js +10 -8
  209. package/dist/src/ui/contexts/TodoProvider.js.map +1 -1
  210. package/dist/src/ui/contexts/ToolCallProvider.d.ts +1 -0
  211. package/dist/src/ui/contexts/ToolCallProvider.js +10 -9
  212. package/dist/src/ui/contexts/ToolCallProvider.js.map +1 -1
  213. package/dist/src/ui/hooks/__tests__/useSlashCompletion.set.phase09.test.d.ts +6 -0
  214. package/dist/src/ui/hooks/__tests__/useSlashCompletion.set.phase09.test.js +39 -0
  215. package/dist/src/ui/hooks/__tests__/useSlashCompletion.set.phase09.test.js.map +1 -0
  216. package/dist/src/ui/hooks/atCommandProcessor.js +11 -3
  217. package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
  218. package/dist/src/ui/hooks/atCommandProcessor.test.js +3 -1
  219. package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
  220. package/dist/src/ui/hooks/shellCommandProcessor.js +4 -1
  221. package/dist/src/ui/hooks/shellCommandProcessor.js.map +1 -1
  222. package/dist/src/ui/hooks/shellCommandProcessor.test.js +1 -0
  223. package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -1
  224. package/dist/src/ui/hooks/slashCommandProcessor.js +27 -1
  225. package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
  226. package/dist/src/ui/hooks/useAuthCommand.js +11 -3
  227. package/dist/src/ui/hooks/useAuthCommand.js.map +1 -1
  228. package/dist/src/ui/hooks/useCommandCompletion.d.ts +1 -0
  229. package/dist/src/ui/hooks/useCommandCompletion.js +2 -0
  230. package/dist/src/ui/hooks/useCommandCompletion.js.map +1 -1
  231. package/dist/src/ui/hooks/useGeminiStream.js +37 -11
  232. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  233. package/dist/src/ui/hooks/useGeminiStream.subagent.spec.d.ts +6 -0
  234. package/dist/src/ui/hooks/useGeminiStream.subagent.spec.js +232 -0
  235. package/dist/src/ui/hooks/useGeminiStream.subagent.spec.js.map +1 -0
  236. package/dist/src/ui/hooks/useLoadProfileDialog.d.ts +1 -1
  237. package/dist/src/ui/hooks/useLoadProfileDialog.js +18 -57
  238. package/dist/src/ui/hooks/useLoadProfileDialog.js.map +1 -1
  239. package/dist/src/ui/hooks/useOpenAIProviderInfo.d.ts +1 -1
  240. package/dist/src/ui/hooks/useOpenAIProviderInfo.js +12 -7
  241. package/dist/src/ui/hooks/useOpenAIProviderInfo.js.map +1 -1
  242. package/dist/src/ui/hooks/useProviderDialog.d.ts +1 -1
  243. package/dist/src/ui/hooks/useProviderDialog.js +17 -90
  244. package/dist/src/ui/hooks/useProviderDialog.js.map +1 -1
  245. package/dist/src/ui/hooks/useProviderModelDialog.d.ts +2 -2
  246. package/dist/src/ui/hooks/useProviderModelDialog.js +11 -12
  247. package/dist/src/ui/hooks/useProviderModelDialog.js.map +1 -1
  248. package/dist/src/ui/hooks/useReactToolScheduler.d.ts +3 -1
  249. package/dist/src/ui/hooks/useReactToolScheduler.js +144 -34
  250. package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
  251. package/dist/src/ui/hooks/useSlashCompletion.d.ts +32 -0
  252. package/dist/src/ui/hooks/useSlashCompletion.js +154 -77
  253. package/dist/src/ui/hooks/useSlashCompletion.js.map +1 -1
  254. package/dist/src/ui/hooks/useSlashCompletion.test.js +39 -14
  255. package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
  256. package/dist/src/ui/hooks/useToolScheduler.test.js +108 -79
  257. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  258. package/dist/src/ui/types.d.ts +1 -0
  259. package/dist/src/ui/types.js.map +1 -1
  260. package/dist/src/utils/sandbox.js +7 -5
  261. package/dist/src/utils/sandbox.js.map +1 -1
  262. package/dist/src/validateNonInterActiveAuth.js +4 -2
  263. package/dist/src/validateNonInterActiveAuth.js.map +1 -1
  264. package/dist/src/zed-integration/schema.d.ts +30 -30
  265. package/dist/src/zed-integration/zedIntegration.js +112 -39
  266. package/dist/src/zed-integration/zedIntegration.js.map +1 -1
  267. package/dist/tsconfig.build.tsbuildinfo +1 -0
  268. package/package.json +5 -3
  269. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -3,785 +3,110 @@
3
3
  * Copyright 2025 Vybestack LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
7
- import fs from 'fs/promises';
8
- import os from 'os';
9
- import path from 'path';
6
+ import { beforeEach, afterEach, describe, expect, it } from 'vitest';
7
+ import { Config, ProviderManager, } from '@vybestack/llxprt-code-core';
10
8
  import { createMockCommandContext } from '../test-utils/mockCommandContext.js';
11
9
  import { setCommand } from '../ui/commands/setCommand.js';
12
- import { SettingScope } from '../config/settings.js';
13
- // Stub implementations for missing commands
14
- const saveCommand = {
15
- action: vi.fn().mockResolvedValue({
16
- type: 'message',
17
- messageType: 'success',
18
- content: 'Profile saved',
19
- }),
20
- };
21
- const loadCommand = {
22
- action: vi.fn().mockResolvedValue({
23
- type: 'message',
24
- messageType: 'success',
25
- content: 'Profile loaded',
26
- }),
27
- };
28
- import { getProviderManager } from '../providers/providerManagerInstance.js';
29
- import { ProfileManager, } from '@vybestack/llxprt-code-core';
30
- // Mock modules
31
- vi.mock('fs/promises');
32
- vi.mock('os', async (importOriginal) => {
33
- const actual = await importOriginal();
10
+ import { setCliRuntimeContext, registerCliProviderInfrastructure, resetCliProviderInfrastructure, getActiveModelParams, buildRuntimeProfileSnapshot, clearActiveModelParam, } from '../runtime/runtimeSettings.js';
11
+ import { createTempDirectory, cleanupTempDirectory } from './test-utils.js';
12
+ function createStubProvider(name) {
34
13
  return {
35
- ...actual,
36
- homedir: vi.fn().mockReturnValue('/home/testuser'),
37
- };
38
- });
39
- // Mock provider manager
40
- vi.mock('../providers/providerManagerInstance.js', () => ({
41
- getProviderManager: vi.fn(),
42
- }));
43
- let mockOpenAIClient;
44
- vi.mock('openai', () => ({
45
- default: vi.fn().mockImplementation((config) => {
46
- mockOpenAIClient = {
47
- config,
48
- chat: {
49
- completions: {
50
- create: vi.fn().mockResolvedValue({
51
- choices: [
52
- {
53
- message: {
54
- content: 'Test response',
55
- role: 'assistant',
56
- },
57
- },
58
- ],
59
- }),
14
+ name,
15
+ async getModels() {
16
+ return [
17
+ {
18
+ id: `${name}-model`,
19
+ name: `${name}-model`,
20
+ provider: name,
21
+ supportedToolFormats: [],
60
22
  },
61
- },
62
- };
63
- return mockOpenAIClient;
64
- }),
65
- }));
66
- describe('Model Parameters and Profiles Integration Tests', () => {
67
- let mockProvider;
68
- let mockProviderManager;
69
- let context;
70
- let config;
71
- let settings;
72
- const mockHomedir = '/home/testuser';
73
- const profilesDir = path.join(mockHomedir, '.llxprt', 'profiles');
74
- // Sample ephemeral settings for testing
75
- const testEphemeralSettings = {
76
- 'context-limit': 32000,
77
- 'compression-threshold': 0.85,
78
- 'auth-keyfile': '~/.keys/openai-key',
79
- 'base-url': 'https://api.openai.com/v1',
80
- 'tool-format': 'openai',
81
- 'api-version': '2024-01-01',
82
- 'custom-headers': {
83
- 'X-Custom-Header': 'test-value',
84
- Authorization: 'Bearer custom-token',
23
+ ];
24
+ },
25
+ getDefaultModel() {
26
+ return `${name}-model`;
27
+ },
28
+ async *generateChatCompletion() {
29
+ yield {
30
+ speaker: 'ai',
31
+ blocks: [{ type: 'text', text: `response-from-${name}` }],
32
+ };
33
+ },
34
+ getServerTools() {
35
+ return [];
36
+ },
37
+ async invokeServerTool() {
38
+ return {};
85
39
  },
86
40
  };
41
+ }
42
+ describe('CLI model parameter command integration', () => {
43
+ let tempDir;
44
+ let config;
45
+ let settingsService;
46
+ let providerManager;
47
+ let context;
48
+ const runSetCommand = async (args) => {
49
+ if (!setCommand.action) {
50
+ throw new Error('setCommand.action is not defined');
51
+ }
52
+ return setCommand.action(context, args);
53
+ };
87
54
  beforeEach(async () => {
88
- // Setup file system mocks
89
- vi.mocked(os.homedir).mockReturnValue(mockHomedir);
90
- vi.mocked(fs.mkdir).mockResolvedValue(undefined);
91
- vi.mocked(fs.writeFile).mockResolvedValue(undefined);
92
- vi.mocked(fs.readFile).mockResolvedValue('{}');
93
- vi.mocked(fs.access).mockResolvedValue(undefined);
94
- // Reset OpenAI mock
95
- mockOpenAIClient = null;
96
- // Create mock provider with full implementation
97
- mockProvider = {
98
- name: 'openai',
99
- getModels: vi.fn().mockResolvedValue([
100
- { id: 'gpt-4', name: 'GPT-4' },
101
- { id: 'gpt-4o-mini', name: 'GPT-4.0 Mini' },
102
- ]),
103
- generateChatCompletion: vi.fn(),
104
- getServerTools: vi.fn().mockReturnValue([]),
105
- invokeServerTool: vi.fn().mockResolvedValue(undefined),
106
- setModelParams: vi.fn(),
107
- getModelParams: vi.fn().mockReturnValue({}),
108
- setModel: vi.fn(),
109
- getDefaultModel: vi.fn().mockReturnValue('gpt-4'),
110
- setConfig: vi.fn(),
111
- setApiKey: vi.fn(),
112
- };
113
- // Create mock provider manager
114
- mockProviderManager = {
115
- getActiveProvider: vi.fn().mockReturnValue(mockProvider),
116
- setActiveProvider: vi.fn().mockImplementation((providerName) => {
117
- if (providerName !== 'openai' && providerName !== 'anthropic') {
118
- throw new Error(`Provider not found: ${providerName}`);
119
- }
120
- mockProvider.name = providerName;
121
- }),
122
- getProvider: vi.fn().mockImplementation((name) => {
123
- if (name === 'openai' || name === 'anthropic') {
124
- return mockProvider;
125
- }
126
- return null;
127
- }),
128
- };
129
- vi.mocked(getProviderManager).mockReturnValue(mockProviderManager);
130
- // Load real settings and config
131
- settings = {
132
- merged: { ...testEphemeralSettings },
133
- setValue: vi
134
- .fn()
135
- .mockImplementation((scope, key, value) => {
136
- // Simulate setting values
137
- settings.merged[key] = value;
138
- }),
139
- errors: [],
140
- };
141
- // Create config with necessary methods
142
- const mockConfig = {
143
- getProviderManager: vi.fn().mockReturnValue(mockProviderManager),
144
- getProvider: vi.fn().mockReturnValue('openai'),
145
- setModel: vi.fn(),
146
- getDefaultModel: vi.fn().mockReturnValue('gpt-4'),
147
- initialize: vi.fn().mockResolvedValue(undefined),
148
- getDebugMode: vi.fn().mockReturnValue(false),
149
- };
150
- config = mockConfig;
151
- // Create context for commands
55
+ tempDir = await createTempDirectory();
56
+ config = new Config({
57
+ sessionId: 'model-params-integration',
58
+ targetDir: tempDir,
59
+ debugMode: false,
60
+ cwd: tempDir,
61
+ model: 'alpha-model',
62
+ });
63
+ await config.initialize();
64
+ settingsService = config.getSettingsService();
65
+ providerManager = new ProviderManager({ settingsService, config });
66
+ providerManager.registerProvider(createStubProvider('alpha'));
67
+ providerManager.setActiveProvider('alpha');
68
+ registerCliProviderInfrastructure(providerManager, {});
69
+ setCliRuntimeContext(settingsService, config, {
70
+ metadata: { source: 'modelParams.integration.test.ts' },
71
+ });
152
72
  context = createMockCommandContext({
153
73
  services: {
154
74
  config: config,
155
- settings: settings,
75
+ settings: settingsService,
156
76
  },
157
77
  });
158
78
  });
159
- afterEach(() => {
160
- vi.clearAllMocks();
79
+ afterEach(async () => {
80
+ resetCliProviderInfrastructure();
81
+ await cleanupTempDirectory(tempDir);
161
82
  });
162
- describe('Full Workflow: Set Save Load → Use', () => {
163
- it('should complete full workflow with model parameters and profiles', async () => {
164
- // Step 1: Set multiple model parameters
165
- let result = await setCommand.action(context, 'modelparam temperature 0.8');
166
- expect(result).toEqual({
167
- type: 'message',
168
- messageType: 'info',
169
- content: "Model parameter 'temperature' set to 0.8",
170
- });
171
- expect(mockProvider.setModelParams).toHaveBeenCalledWith({
172
- temperature: 0.8,
173
- });
174
- result = await setCommand.action(context, 'modelparam max_tokens 4096');
175
- expect(mockProvider.setModelParams).toHaveBeenCalledWith({
176
- max_tokens: 4096,
177
- });
178
- result = await setCommand.action(context, 'modelparam top_p 0.95');
179
- expect(mockProvider.setModelParams).toHaveBeenCalledWith({ top_p: 0.95 });
180
- // Mock the provider returning all set parameters
181
- mockProvider.getModelParams = vi.fn().mockReturnValue({
182
- temperature: 0.8,
183
- max_tokens: 4096,
184
- top_p: 0.95,
185
- });
186
- // Step 2: Save profile
187
- result = await saveCommand.action(context, '"Production Profile"');
188
- expect(result).toEqual({
189
- type: 'message',
190
- messageType: 'info',
191
- content: "Profile 'Production Profile' saved",
192
- });
193
- // Verify profile was saved with correct structure
194
- expect(vi.mocked(fs.writeFile)).toHaveBeenCalledWith(path.join(profilesDir, 'Production Profile.json'), expect.stringContaining('"temperature": 0.8'), 'utf8');
195
- const savedProfileContent = vi.mocked(fs.writeFile).mock.calls[0][1];
196
- const savedProfile = JSON.parse(savedProfileContent);
197
- expect(savedProfile).toMatchObject({
198
- version: 1,
199
- provider: 'openai',
200
- model: 'gpt-4',
201
- modelParams: {
202
- temperature: 0.8,
203
- max_tokens: 4096,
204
- top_p: 0.95,
205
- },
206
- ephemeralSettings: expect.objectContaining({
207
- 'context-limit': 32000,
208
- 'compression-threshold': 0.85,
209
- }),
210
- });
211
- // Verify the savedProfile actually has the ephemeral settings
212
- expect(Object.keys(savedProfile.ephemeralSettings).length).toBeGreaterThan(0);
213
- // Step 3: Reset state and load profile
214
- mockProvider.setModelParams = vi.fn();
215
- mockProvider.getModelParams = vi.fn().mockReturnValue({});
216
- // Create a fresh settings mock for loading
217
- const loadSettings = {
218
- merged: {},
219
- setValue: vi
220
- .fn()
221
- .mockImplementation((scope, key, value) => {
222
- loadSettings.merged[key] = value;
223
- }),
224
- };
225
- // Update context with fresh settings
226
- context.services.settings =
227
- loadSettings;
228
- // Mock reading the saved profile
229
- vi.mocked(fs.readFile).mockResolvedValue(savedProfileContent);
230
- result = await loadCommand.action(context, '"Production Profile"');
231
- expect(result).toEqual({
232
- type: 'message',
233
- messageType: 'info',
234
- content: "Profile 'Production Profile' loaded",
235
- });
236
- // Verify all settings were applied
237
- expect(mockProviderManager.setActiveProvider).toHaveBeenCalledWith('openai');
238
- expect(config.setModel).toHaveBeenCalledWith('gpt-4');
239
- expect(mockProvider.setModelParams).toHaveBeenCalledWith({
240
- temperature: 0.8,
241
- max_tokens: 4096,
242
- top_p: 0.95,
243
- });
244
- // Verify ephemeral settings were applied
245
- // Count the actual number of ephemeral settings in the saved profile
246
- const numEphemeralSettings = Object.keys(savedProfile.ephemeralSettings).length;
247
- expect(loadSettings.setValue).toHaveBeenCalledTimes(numEphemeralSettings);
248
- expect(loadSettings.setValue).toHaveBeenCalledWith(SettingScope.User, 'context-limit', 32000);
249
- expect(loadSettings.setValue).toHaveBeenCalledWith(SettingScope.User, 'compression-threshold', 0.85);
250
- // Step 4: Verify parameters are used in API calls
251
- // This would happen when the provider makes actual API calls
252
- // In a real scenario, the OpenAI client would receive these parameters
253
- });
254
- it('should handle complex JSON parameters in full workflow', async () => {
255
- // Set complex parameters including JSON
256
- await setCommand.action(context, 'modelparam response_format {"type":"json_object"}');
257
- await setCommand.action(context, 'modelparam custom-headers {"X-API-Key":"secret","X-Request-ID":"123"}');
258
- mockProvider.getModelParams = vi.fn().mockReturnValue({
259
- response_format: { type: 'json_object' },
260
- 'custom-headers': { 'X-API-Key': 'secret', 'X-Request-ID': '123' },
261
- });
262
- // Save profile
263
- const result = await saveCommand.action(context, '"JSON Test Profile"');
264
- expect(result?.messageType).toBe('info');
265
- // Verify JSON was preserved correctly
266
- const savedContent = vi.mocked(fs.writeFile).mock.calls[0][1];
267
- const savedProfile = JSON.parse(savedContent);
268
- expect(savedProfile.modelParams).toEqual({
269
- response_format: { type: 'json_object' },
270
- 'custom-headers': { 'X-API-Key': 'secret', 'X-Request-ID': '123' },
271
- });
272
- // Load and verify JSON parameters are restored
273
- vi.mocked(fs.readFile).mockResolvedValue(savedContent);
274
- await loadCommand.action(context, '"JSON Test Profile"');
275
- expect(mockProvider.setModelParams).toHaveBeenCalledWith({
276
- response_format: { type: 'json_object' },
277
- 'custom-headers': { 'X-API-Key': 'secret', 'X-Request-ID': '123' },
278
- });
279
- });
83
+ it('sets provider-scoped model params via /set modelparam', async () => {
84
+ await runSetCommand('modelparam temperature 0.9');
85
+ expect(getActiveModelParams()).toEqual({ temperature: 0.9 });
86
+ expect(settingsService.getProviderSettings('alpha').temperature).toBe(0.9);
280
87
  });
281
- describe('Multiple Profiles with Different Settings', () => {
282
- it('should manage multiple profiles independently', async () => {
283
- // Create Development Profile
284
- mockProvider.getModelParams = vi.fn().mockReturnValue({
285
- temperature: 0.9,
286
- max_tokens: 8192,
287
- stream: true,
288
- });
289
- context.services.settings.merged = {
290
- 'context-limit': 64000,
291
- 'compression-threshold': 0.9,
292
- 'tool-format': 'openai',
293
- };
294
- await saveCommand.action(context, '"Development Profile"');
295
- // Create Production Profile
296
- mockProvider.getModelParams = vi.fn().mockReturnValue({
297
- temperature: 0.3,
298
- max_tokens: 2048,
299
- stream: false,
300
- seed: 42,
301
- });
302
- context.services.settings.merged = {
303
- 'context-limit': 16000,
304
- 'compression-threshold': 0.7,
305
- 'tool-format': 'openai',
306
- };
307
- await saveCommand.action(context, '"Production Profile"');
308
- // Verify both profiles were saved
309
- expect(vi.mocked(fs.writeFile)).toHaveBeenCalledTimes(2);
310
- // Load Development Profile
311
- const devProfileContent = vi.mocked(fs.writeFile).mock.calls[0][1];
312
- vi.mocked(fs.readFile).mockResolvedValue(devProfileContent);
313
- mockProvider.setModelParams = vi.fn();
314
- const devSettings = {
315
- merged: {},
316
- setValue: vi
317
- .fn()
318
- .mockImplementation((scope, key, value) => {
319
- devSettings.merged[key] = value;
320
- }),
321
- };
322
- context.services.settings =
323
- devSettings;
324
- await loadCommand.action(context, '"Development Profile"');
325
- expect(mockProvider.setModelParams).toHaveBeenCalledWith({
326
- temperature: 0.9,
327
- max_tokens: 8192,
328
- stream: true,
329
- });
330
- expect(devSettings.setValue).toHaveBeenCalledTimes(3); // 3 ephemeral settings
331
- expect(devSettings.setValue).toHaveBeenCalledWith(SettingScope.User, 'context-limit', 64000);
332
- // Load Production Profile
333
- const prodProfileContent = vi.mocked(fs.writeFile).mock.calls[1][1];
334
- vi.mocked(fs.readFile).mockResolvedValue(prodProfileContent);
335
- mockProvider.setModelParams = vi.fn();
336
- const prodSettings = {
337
- merged: {},
338
- setValue: vi
339
- .fn()
340
- .mockImplementation((scope, key, value) => {
341
- prodSettings.merged[key] = value;
342
- }),
343
- };
344
- context.services.settings =
345
- prodSettings;
346
- await loadCommand.action(context, '"Production Profile"');
347
- expect(mockProvider.setModelParams).toHaveBeenCalledWith({
348
- temperature: 0.3,
349
- max_tokens: 2048,
350
- stream: false,
351
- seed: 42,
352
- });
353
- expect(prodSettings.setValue).toHaveBeenCalledTimes(3); // 3 ephemeral settings
354
- expect(prodSettings.setValue).toHaveBeenCalledWith(SettingScope.User, 'context-limit', 16000);
355
- });
356
- it('should handle profile names with special characters', async () => {
357
- const specialNames = [
358
- 'Profile-2024',
359
- 'Test_Profile',
360
- 'My.Profile',
361
- 'Profile (Dev)',
362
- 'Profile & Testing',
363
- '测试配置', // Unicode characters
364
- ];
365
- for (const name of specialNames) {
366
- mockProvider.getModelParams = vi.fn().mockReturnValue({
367
- temperature: 0.5,
368
- });
369
- const result = await saveCommand.action(context, `"${name}"`);
370
- expect(result).toEqual({
371
- type: 'message',
372
- messageType: 'info',
373
- content: `Profile '${name}' saved`,
374
- });
375
- // Verify file was created with correct name
376
- expect(vi.mocked(fs.writeFile)).toHaveBeenCalledWith(path.join(profilesDir, `${name}.json`), expect.any(String), 'utf8');
377
- }
378
- });
88
+ it('clears model params using /set unset modelparam', async () => {
89
+ await runSetCommand('modelparam max_tokens 4096');
90
+ expect(getActiveModelParams()).toEqual({ max_tokens: 4096 });
91
+ await runSetCommand('unset modelparam max_tokens');
92
+ expect(getActiveModelParams()).toEqual({});
93
+ expect(settingsService.getProviderSettings('alpha').max_tokens).toBeUndefined();
379
94
  });
380
- describe('Switching Between Providers with Profiles', () => {
381
- it('should switch providers when loading profiles', async () => {
382
- // Create OpenAI profile
383
- mockProvider.name = 'openai';
384
- mockProvider.getModelParams = vi.fn().mockReturnValue({
385
- temperature: 0.7,
386
- max_tokens: 4096,
387
- });
388
- await saveCommand.action(context, '"OpenAI Profile"');
389
- // Create Anthropic profile
390
- const anthropicProvider = {
391
- ...mockProvider,
392
- name: 'anthropic',
393
- getModelParams: vi.fn().mockReturnValue({
394
- temperature: 0.5,
395
- max_output_tokens: 4096,
396
- }),
397
- };
398
- mockProviderManager.getActiveProvider.mockReturnValue(anthropicProvider);
399
- config.getProvider = vi.fn().mockReturnValue('anthropic');
400
- config.getModel = vi.fn().mockReturnValue('claude-3-opus-20240229');
401
- await saveCommand.action(context, '"Anthropic Profile"');
402
- // Load OpenAI profile
403
- const openaiContent = vi.mocked(fs.writeFile).mock.calls[0][1];
404
- vi.mocked(fs.readFile).mockResolvedValue(openaiContent);
405
- await loadCommand.action(context, '"OpenAI Profile"');
406
- expect(mockProviderManager.setActiveProvider).toHaveBeenCalledWith('openai');
407
- expect(config.setModel).toHaveBeenCalledWith('gpt-4');
408
- // Load Anthropic profile
409
- const anthropicContent = vi.mocked(fs.writeFile).mock
410
- .calls[1][1];
411
- vi.mocked(fs.readFile).mockResolvedValue(anthropicContent);
412
- await loadCommand.action(context, '"Anthropic Profile"');
413
- expect(mockProviderManager.setActiveProvider).toHaveBeenCalledWith('anthropic');
414
- expect(config.setModel).toHaveBeenCalledWith('claude-3-opus-20240229');
415
- });
416
- it('should handle provider-specific parameter differences', async () => {
417
- // OpenAI uses max_tokens, Anthropic uses max_output_tokens
418
- const providers = [
419
- {
420
- name: 'openai',
421
- params: { temperature: 0.7, max_tokens: 4096 },
422
- model: 'gpt-4',
423
- },
424
- {
425
- name: 'anthropic',
426
- params: { temperature: 0.5, max_output_tokens: 4096 },
427
- model: 'claude-3-opus-20240229',
428
- },
429
- ];
430
- for (const providerConfig of providers) {
431
- mockProvider.name = providerConfig.name;
432
- mockProvider.getModelParams = vi
433
- .fn()
434
- .mockReturnValue(providerConfig.params);
435
- config.getProvider = vi.fn().mockReturnValue(providerConfig.name);
436
- config.getModel = vi.fn().mockReturnValue(providerConfig.model);
437
- await saveCommand.action(context, `"${providerConfig.name} Profile"`);
438
- }
439
- // Verify each profile has correct provider-specific params
440
- const savedProfiles = vi
441
- .mocked(fs.writeFile)
442
- .mock.calls.map((call) => JSON.parse(call[1]));
443
- expect(savedProfiles[0]).toMatchObject({
444
- provider: 'openai',
445
- modelParams: { temperature: 0.7, max_tokens: 4096 },
446
- });
447
- expect(savedProfiles[1]).toMatchObject({
448
- provider: 'anthropic',
449
- modelParams: { temperature: 0.5, max_output_tokens: 4096 },
450
- });
95
+ it('produces runtime profile snapshots that include current model params', async () => {
96
+ await runSetCommand('modelparam response_format {"type":"json_object"}');
97
+ await runSetCommand('modelparam top_p 0.92');
98
+ const snapshot = buildRuntimeProfileSnapshot();
99
+ expect(snapshot.provider).toBe('alpha');
100
+ expect(snapshot.modelParams).toEqual({
101
+ response_format: { type: 'json_object' },
102
+ top_p: 0.92,
451
103
  });
452
104
  });
453
- describe('CLI Mode with --load Flag', () => {
454
- it('should load profile in non-interactive mode', async () => {
455
- // Create a profile to load
456
- const testProfile = {
457
- version: 1,
458
- provider: 'openai',
459
- model: 'gpt-4',
460
- modelParams: {
461
- temperature: 0.7,
462
- max_tokens: 4096,
463
- },
464
- ephemeralSettings: {
465
- 'context-limit': 32000,
466
- 'base-url': 'https://api.openai.com/v1',
467
- },
468
- };
469
- vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(testProfile));
470
- // Simulate loading profile via CLI flag
471
- const profileManager = new ProfileManager();
472
- const loadedProfile = await profileManager.loadProfile('CLI Profile');
473
- expect(loadedProfile).toEqual(testProfile);
474
- // In actual CLI mode, these would be applied during initialization
475
- // The config would use these values when making API calls
476
- });
477
- it('should apply profile settings before processing prompt', async () => {
478
- // This tests the order of operations in CLI mode
479
- const callOrder = [];
480
- // Mock profile loading
481
- const testProfile = {
482
- version: 1,
483
- provider: 'openai',
484
- model: 'gpt-4o-mini',
485
- modelParams: {
486
- temperature: 0.5,
487
- max_tokens: 2048,
488
- },
489
- ephemeralSettings: {
490
- 'context-limit': 16000,
491
- },
492
- };
493
- vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(testProfile));
494
- // Mock provider operations to track order
495
- vi.mocked(mockProviderManager.setActiveProvider).mockImplementation(() => {
496
- callOrder.push('setActiveProvider');
497
- });
498
- if (mockProvider.setModelParams) {
499
- vi.mocked(mockProvider.setModelParams).mockImplementation(() => {
500
- callOrder.push('setModelParams');
501
- });
502
- }
503
- vi.mocked(mockProvider.generateChatCompletion).mockImplementation(async function* () {
504
- callOrder.push('generateChatCompletion');
505
- yield {
506
- speaker: 'ai',
507
- blocks: [{ type: 'text', text: 'Response' }],
508
- };
509
- });
510
- // Simulate CLI initialization sequence
511
- await mockProviderManager.setActiveProvider('openai');
512
- await mockProvider.setModelParams?.(testProfile.modelParams);
513
- await mockProvider.generateChatCompletion?.({}, {});
514
- expect(callOrder).toEqual([
515
- 'setActiveProvider',
516
- 'setModelParams',
517
- 'generateChatCompletion',
518
- ]);
519
- });
520
- });
521
- describe('Override Behavior (Load then Modify)', () => {
522
- it('should allow modifying parameters after loading profile', async () => {
523
- // Load a profile
524
- const baseProfile = {
525
- version: 1,
526
- provider: 'openai',
527
- model: 'gpt-4',
528
- modelParams: {
529
- temperature: 0.7,
530
- max_tokens: 4096,
531
- top_p: 0.95,
532
- },
533
- ephemeralSettings: {
534
- 'context-limit': 32000,
535
- },
536
- };
537
- vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(baseProfile));
538
- await loadCommand.action(context, '"Base Profile"');
539
- // Verify profile was loaded
540
- expect(mockProvider.setModelParams).toHaveBeenCalledWith(baseProfile.modelParams);
541
- // Override specific parameters
542
- mockProvider.setModelParams = vi.fn();
543
- await setCommand.action(context, 'modelparam temperature 0.9');
544
- expect(mockProvider.setModelParams).toHaveBeenCalledWith({
545
- temperature: 0.9,
546
- });
547
- await setCommand.action(context, 'modelparam max_tokens 8192');
548
- expect(mockProvider.setModelParams).toHaveBeenCalledWith({
549
- max_tokens: 8192,
550
- });
551
- // Original profile remains unchanged
552
- const reloadedProfile = JSON.parse(await fs.readFile(path.join(profilesDir, 'Base Profile.json'), 'utf8'));
553
- expect(reloadedProfile.modelParams.temperature).toBe(0.7);
554
- expect(reloadedProfile.modelParams.max_tokens).toBe(4096);
555
- });
556
- it('should allow switching providers after loading profile', async () => {
557
- // Load OpenAI profile
558
- const openaiProfile = {
559
- version: 1,
560
- provider: 'openai',
561
- model: 'gpt-4',
562
- modelParams: {
563
- temperature: 0.7,
564
- },
565
- ephemeralSettings: {},
566
- };
567
- vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(openaiProfile));
568
- await loadCommand.action(context, '"OpenAI Profile"');
569
- expect(mockProviderManager.setActiveProvider).toHaveBeenCalledWith('openai');
570
- // Switch to Anthropic manually
571
- mockProviderManager.setActiveProvider = vi.fn();
572
- await mockProviderManager.setActiveProvider('anthropic');
573
- expect(mockProviderManager.setActiveProvider).toHaveBeenCalledWith('anthropic');
574
- // Set Anthropic-specific parameters
575
- await setCommand.action(context, 'modelparam max_output_tokens 4096');
576
- expect(mockProvider.setModelParams).toHaveBeenCalledWith({
577
- max_output_tokens: 4096,
578
- });
579
- });
580
- });
581
- describe('Edge Cases', () => {
582
- it('should handle missing provider support gracefully', async () => {
583
- // Provider without model params support
584
- const basicProvider = {
585
- name: 'basic-provider',
586
- getModels: vi.fn().mockResolvedValue([]),
587
- generateChatCompletion: vi.fn(),
588
- getServerTools: vi.fn().mockReturnValue([]),
589
- invokeServerTool: vi.fn().mockResolvedValue(undefined),
590
- // No setModelParams or getModelParams
591
- };
592
- mockProviderManager.getActiveProvider.mockReturnValue(basicProvider);
593
- // Try to set model params
594
- const result = await setCommand.action(context, 'modelparam temperature 0.7');
595
- expect(result).toEqual({
596
- type: 'message',
597
- messageType: 'error',
598
- content: "Provider 'basic-provider' does not support model parameters",
599
- });
600
- // Can still save profile without model params
601
- await saveCommand.action(context, '"Basic Provider Profile"');
602
- const savedContent = vi.mocked(fs.writeFile).mock.calls[0][1];
603
- const savedProfile = JSON.parse(savedContent);
604
- expect(savedProfile.modelParams).toEqual({});
605
- });
606
- it('should handle corrupted profile files', async () => {
607
- const corruptedCases = [
608
- { content: '{ invalid json }', error: 'corrupted' },
609
- { content: '{}', error: 'invalid: missing required fields' },
610
- {
611
- content: JSON.stringify({
612
- version: 2,
613
- provider: 'openai',
614
- model: 'gpt-4',
615
- modelParams: {},
616
- ephemeralSettings: {},
617
- }),
618
- error: 'Unsupported profile version',
619
- },
620
- {
621
- content: JSON.stringify({ version: 1, provider: 'openai' }),
622
- error: 'invalid: missing required fields',
623
- },
624
- ];
625
- for (const testCase of corruptedCases) {
626
- vi.mocked(fs.readFile).mockResolvedValue(testCase.content);
627
- const result = await loadCommand.action(context, '"Corrupted Profile"');
628
- expect(result?.messageType).toBe('error');
629
- // The error messages come with 'Failed to load profile: ' prefix
630
- if (testCase.error === 'Unsupported profile version') {
631
- expect(result?.content?.toLowerCase()).toContain(testCase.error.toLowerCase());
632
- }
633
- else {
634
- expect(result?.content).toContain(testCase.error);
635
- }
636
- }
637
- });
638
- it('should handle file system errors during save/load', async () => {
639
- // Permission error during save
640
- vi.mocked(fs.writeFile).mockRejectedValue(new Error('EACCES: permission denied'));
641
- let result = await saveCommand.action(context, '"Permission Test"');
642
- expect(result).toEqual({
643
- type: 'message',
644
- messageType: 'error',
645
- content: 'Failed to save profile: EACCES: permission denied',
646
- });
647
- // File not found during load
648
- vi.mocked(fs.readFile).mockRejectedValue(new Error('ENOENT: no such file or directory'));
649
- result = await loadCommand.action(context, '"Missing Profile"');
650
- expect(result).toEqual({
651
- type: 'message',
652
- messageType: 'error',
653
- content: "Profile 'Missing Profile' not found",
654
- });
655
- });
656
- it('should handle very large parameter sets', async () => {
657
- // Create a profile with many parameters
658
- const largeParams = {};
659
- for (let i = 0; i < 100; i++) {
660
- largeParams[`param_${i}`] = i % 2 === 0 ? i * 0.1 : `value_${i}`;
661
- }
662
- mockProvider.getModelParams = vi.fn().mockReturnValue(largeParams);
663
- await saveCommand.action(context, '"Large Profile"');
664
- // Verify all parameters were saved
665
- const savedContent = vi.mocked(fs.writeFile).mock.calls[0][1];
666
- const savedProfile = JSON.parse(savedContent);
667
- expect(Object.keys(savedProfile.modelParams)).toHaveLength(100);
668
- // Load and verify all parameters are restored
669
- vi.mocked(fs.readFile).mockResolvedValue(savedContent);
670
- mockProvider.setModelParams = vi.fn();
671
- await loadCommand.action(context, '"Large Profile"');
672
- expect(mockProvider.setModelParams).toHaveBeenCalledWith(largeParams);
673
- });
674
- it('should preserve numeric precision in parameters', async () => {
675
- const preciseParams = {
676
- temperature: 0.123456789,
677
- top_p: 0.999999999,
678
- frequency_penalty: -0.000000001,
679
- seed: 9007199254740991, // Max safe integer
680
- };
681
- mockProvider.getModelParams = vi.fn().mockReturnValue(preciseParams);
682
- await saveCommand.action(context, '"Precision Profile"');
683
- const savedContent = vi.mocked(fs.writeFile).mock.calls[0][1];
684
- vi.mocked(fs.readFile).mockResolvedValue(savedContent);
685
- mockProvider.setModelParams = vi.fn();
686
- await loadCommand.action(context, '"Precision Profile"');
687
- // Verify precision was preserved
688
- const mockCalls = vi.mocked(mockProvider.setModelParams).mock.calls;
689
- expect(mockCalls).toHaveLength(1);
690
- const calledParams = mockCalls[0]?.[0];
691
- expect(calledParams).toBeDefined();
692
- expect(calledParams?.temperature).toBe(0.123456789);
693
- expect(calledParams?.top_p).toBe(0.999999999);
694
- expect(calledParams?.frequency_penalty).toBe(-0.000000001);
695
- expect(calledParams?.seed).toBe(9007199254740991);
696
- });
697
- });
698
- describe('Real Integration with File System', () => {
699
- it('should verify complete data flow from commands to API', async () => {
700
- // This test verifies the entire flow without mocking file operations
701
- // In a real environment, this would create actual files
702
- // Set up a complete workflow
703
- const workflow = async () => {
704
- // 1. Set parameters
705
- await setCommand.action(context, 'modelparam temperature 0.75');
706
- await setCommand.action(context, 'modelparam max_tokens 3000');
707
- await setCommand.action(context, 'modelparam response_format {"type":"json_object"}');
708
- // 2. Set ephemeral settings using settings.setValue
709
- settings.setValue(SettingScope.User, 'context-limit', 25000);
710
- settings.setValue(SettingScope.User, 'compression-threshold', 0.82);
711
- // Update context settings merged to include these values
712
- context.services.settings.merged['context-limit'] = 25000;
713
- context.services.settings.merged['compression-threshold'] = 0.82;
714
- // 3. Save profile
715
- mockProvider.getModelParams = vi.fn().mockReturnValue({
716
- temperature: 0.75,
717
- max_tokens: 3000,
718
- response_format: { type: 'json_object' },
719
- });
720
- await saveCommand.action(context, '"Integration Test Profile"');
721
- // 4. Clear state
722
- mockProvider.setModelParams = vi.fn();
723
- const freshSettings = {
724
- merged: {},
725
- setValue: vi
726
- .fn()
727
- .mockImplementation((scope, key, value) => {
728
- freshSettings.merged[key] = value;
729
- }),
730
- };
731
- context.services.settings =
732
- freshSettings;
733
- // 5. Load profile
734
- const savedProfile = vi.mocked(fs.writeFile).mock.calls[0][1];
735
- vi.mocked(fs.readFile).mockResolvedValue(savedProfile);
736
- await loadCommand.action(context, '"Integration Test Profile"');
737
- // 6. Verify complete restoration
738
- return {
739
- modelParams: vi.mocked(mockProvider.setModelParams).mock.calls[0][0],
740
- ephemeralSettings: vi
741
- .mocked(freshSettings.setValue)
742
- .mock.calls.reduce((acc, call) => {
743
- acc[call[1]] = call[2];
744
- return acc;
745
- }, {}),
746
- };
747
- };
748
- const result = await workflow();
749
- expect(result.modelParams).toEqual({
750
- temperature: 0.75,
751
- max_tokens: 3000,
752
- response_format: { type: 'json_object' },
753
- });
754
- expect(result.ephemeralSettings).toMatchObject({
755
- 'context-limit': 25000,
756
- 'compression-threshold': 0.82,
757
- });
758
- });
759
- });
760
- describe('Profile Manager Integration', () => {
761
- it('should work with ProfileManager for save and load operations', async () => {
762
- const profileManager = new ProfileManager();
763
- // Create a test profile
764
- const testProfile = {
765
- version: 1,
766
- provider: 'openai',
767
- model: 'gpt-4',
768
- modelParams: {
769
- temperature: 0.6,
770
- max_tokens: 5000,
771
- },
772
- ephemeralSettings: {
773
- 'context-limit': 40000,
774
- },
775
- };
776
- // Mock ProfileManager save operation
777
- vi.mocked(fs.writeFile).mockResolvedValue(undefined);
778
- // Save using ProfileManager (simulated)
779
- await fs.writeFile(path.join(profilesDir, 'ProfileManager Test.json'), JSON.stringify(testProfile, null, 2), 'utf8');
780
- // Load using ProfileManager (simulated)
781
- vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(testProfile));
782
- const loadedProfile = await profileManager.loadProfile('ProfileManager Test');
783
- expect(loadedProfile).toEqual(testProfile);
784
- });
105
+ it('supports clearing params directly through helper', async () => {
106
+ await runSetCommand('modelparam temperature 0.7');
107
+ expect(getActiveModelParams()).toEqual({ temperature: 0.7 });
108
+ clearActiveModelParam('temperature');
109
+ expect(getActiveModelParams()).toEqual({});
785
110
  });
786
111
  });
787
112
  //# sourceMappingURL=modelParams.integration.test.js.map