jiva-core 0.3.41 → 0.3.42-dev.b6b238d

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 (71) hide show
  1. package/README.md +28 -7
  2. package/dist/code/agent.d.ts +4 -0
  3. package/dist/code/agent.d.ts.map +1 -1
  4. package/dist/code/agent.js +70 -18
  5. package/dist/code/agent.js.map +1 -1
  6. package/dist/core/agent-interface.d.ts +8 -0
  7. package/dist/core/agent-interface.d.ts.map +1 -1
  8. package/dist/core/agent-spawner.d.ts.map +1 -1
  9. package/dist/core/agent-spawner.js +2 -1
  10. package/dist/core/agent-spawner.js.map +1 -1
  11. package/dist/core/config.d.ts +56 -0
  12. package/dist/core/config.d.ts.map +1 -1
  13. package/dist/core/config.js +4 -0
  14. package/dist/core/config.js.map +1 -1
  15. package/dist/core/conversation-manager.d.ts +4 -3
  16. package/dist/core/conversation-manager.d.ts.map +1 -1
  17. package/dist/core/conversation-manager.js +16 -8
  18. package/dist/core/conversation-manager.js.map +1 -1
  19. package/dist/core/dual-agent.d.ts +3 -0
  20. package/dist/core/dual-agent.d.ts.map +1 -1
  21. package/dist/core/dual-agent.js +15 -4
  22. package/dist/core/dual-agent.js.map +1 -1
  23. package/dist/core/worker-agent.d.ts.map +1 -1
  24. package/dist/core/worker-agent.js +32 -0
  25. package/dist/core/worker-agent.js.map +1 -1
  26. package/dist/core/workspace.d.ts +6 -0
  27. package/dist/core/workspace.d.ts.map +1 -1
  28. package/dist/core/workspace.js +18 -0
  29. package/dist/core/workspace.js.map +1 -1
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +2 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/interfaces/cli/index.js +7 -7
  35. package/dist/interfaces/cli/index.js.map +1 -1
  36. package/dist/interfaces/cli/repl.d.ts.map +1 -1
  37. package/dist/interfaces/cli/repl.js +21 -3
  38. package/dist/interfaces/cli/repl.js.map +1 -1
  39. package/dist/interfaces/cli/setup-wizard.d.ts +5 -3
  40. package/dist/interfaces/cli/setup-wizard.d.ts.map +1 -1
  41. package/dist/interfaces/cli/setup-wizard.js +265 -288
  42. package/dist/interfaces/cli/setup-wizard.js.map +1 -1
  43. package/dist/interfaces/http/middleware/auth.d.ts.map +1 -1
  44. package/dist/interfaces/http/middleware/auth.js +2 -1
  45. package/dist/interfaces/http/middleware/auth.js.map +1 -1
  46. package/dist/interfaces/http/routes/chat.d.ts.map +1 -1
  47. package/dist/interfaces/http/routes/chat.js +20 -0
  48. package/dist/interfaces/http/routes/chat.js.map +1 -1
  49. package/dist/interfaces/http/session-manager.d.ts +6 -0
  50. package/dist/interfaces/http/session-manager.d.ts.map +1 -1
  51. package/dist/interfaces/http/session-manager.js +14 -5
  52. package/dist/interfaces/http/session-manager.js.map +1 -1
  53. package/dist/interfaces/http/websocket-handler.d.ts.map +1 -1
  54. package/dist/interfaces/http/websocket-handler.js +12 -0
  55. package/dist/interfaces/http/websocket-handler.js.map +1 -1
  56. package/dist/models/krutrim.d.ts +8 -66
  57. package/dist/models/krutrim.d.ts.map +1 -1
  58. package/dist/models/krutrim.js +7 -307
  59. package/dist/models/krutrim.js.map +1 -1
  60. package/dist/models/model-client.d.ts +95 -0
  61. package/dist/models/model-client.d.ts.map +1 -0
  62. package/dist/models/model-client.js +317 -0
  63. package/dist/models/model-client.js.map +1 -0
  64. package/dist/models/orchestrator.d.ts +9 -8
  65. package/dist/models/orchestrator.d.ts.map +1 -1
  66. package/dist/models/orchestrator.js +2 -1
  67. package/dist/models/orchestrator.js.map +1 -1
  68. package/dist/storage/types.d.ts +1 -0
  69. package/dist/storage/types.d.ts.map +1 -1
  70. package/dist/storage/types.js.map +1 -1
  71. package/package.json +1 -1
@@ -1,22 +1,87 @@
1
1
  /**
2
2
  * Setup Wizard for first-time configuration
3
+ *
4
+ * Provider-aware: Krutrim, Groq, Sarvam, OpenAI-Compatible
5
+ * - Auto-fills endpoints and model defaults per provider
6
+ * - Asks API key only once per provider (reuses across model roles)
7
+ * - Sets useHarmonyFormat, reasoningEffortStrategy, defaultMaxTokens automatically
3
8
  */
4
9
  import inquirer from 'inquirer';
5
10
  import { configManager } from '../../core/config.js';
6
11
  import { logger } from '../../utils/logger.js';
7
12
  import chalk from 'chalk';
8
- export async function runSetupWizard() {
9
- console.log(chalk.bold.cyan('\n∞ Welcome to Jiva Setup Wizard\n'));
10
- console.log('This wizard will help you configure Jiva for the first time.\n');
11
- // Reasoning Model Configuration
12
- console.log(chalk.bold('Reasoning Model Configuration (gpt-oss-120b)'));
13
- console.log(chalk.gray('This model will be used for reasoning and tool calling.\n'));
14
- const reasoningAnswers = await inquirer.prompt([
13
+ const PROVIDERS = {
14
+ krutrim: {
15
+ label: 'Krutrim',
16
+ endpoint: 'https://cloud.olakrutrim.com/v1/chat/completions',
17
+ reasoningModel: 'gpt-oss-120b',
18
+ multimodalModel: 'Llama-4-Maverick-17B-128E-Instruct',
19
+ toolCallingModel: 'gpt-oss-120b',
20
+ useHarmonyFormat: true,
21
+ reasoningEffortStrategy: 'system_prompt',
22
+ hasMultimodal: true,
23
+ },
24
+ groq: {
25
+ label: 'Groq',
26
+ endpoint: 'https://api.groq.com/openai/v1/chat/completions',
27
+ reasoningModel: 'openai/gpt-oss-120b',
28
+ multimodalModel: 'meta-llama/llama-4-maverick-17b-128e-instruct',
29
+ toolCallingModel: 'meta-llama/llama-4-maverick-17b-128e-instruct',
30
+ useHarmonyFormat: false,
31
+ reasoningEffortStrategy: 'api_param',
32
+ hasMultimodal: true,
33
+ },
34
+ sarvam: {
35
+ label: 'Sarvam',
36
+ endpoint: 'https://api.sarvam.ai/v1/chat/completions',
37
+ reasoningModel: 'sarvam-105b',
38
+ multimodalModel: null, // Sarvam has no multimodal model
39
+ toolCallingModel: 'sarvam-105b',
40
+ useHarmonyFormat: false,
41
+ reasoningEffortStrategy: 'api_param',
42
+ defaultMaxTokens: 8192,
43
+ hasMultimodal: false,
44
+ note: 'Sarvam does not offer a multimodal model. You will need to pick a separate provider for multimodal.',
45
+ },
46
+ 'openai-compatible': {
47
+ label: 'OpenAI-Compatible',
48
+ endpoint: '',
49
+ reasoningModel: '',
50
+ multimodalModel: '',
51
+ toolCallingModel: '',
52
+ useHarmonyFormat: false,
53
+ reasoningEffortStrategy: 'both',
54
+ hasMultimodal: true,
55
+ },
56
+ };
57
+ const PROVIDER_CHOICES = Object.entries(PROVIDERS).map(([value, p]) => ({
58
+ name: p.label,
59
+ value: value,
60
+ }));
61
+ const MULTIMODAL_PROVIDER_CHOICES = PROVIDER_CHOICES.filter(c => c.value !== 'sarvam');
62
+ // ── Helpers ────────────────────────────────────────────────────────────────
63
+ async function askApiKey(provider, collectedKeys) {
64
+ if (collectedKeys.has(provider)) {
65
+ console.log(chalk.gray(` Using ${PROVIDERS[provider].label} API key already entered.\n`));
66
+ return collectedKeys.get(provider);
67
+ }
68
+ const { apiKey } = await inquirer.prompt([
69
+ {
70
+ type: 'password',
71
+ name: 'apiKey',
72
+ message: `${PROVIDERS[provider].label} API Key:`,
73
+ validate: (input) => input.length > 0 || 'API key is required',
74
+ },
75
+ ]);
76
+ collectedKeys.set(provider, apiKey);
77
+ return apiKey;
78
+ }
79
+ async function askEndpointAndKey(collectedKeys) {
80
+ const answers = await inquirer.prompt([
15
81
  {
16
82
  type: 'input',
17
83
  name: 'endpoint',
18
84
  message: 'API Endpoint URL:',
19
- default: 'https://cloud.olakrutrim.com/v1/chat/completions',
20
85
  validate: (input) => {
21
86
  try {
22
87
  new URL(input);
@@ -33,151 +98,194 @@ export async function runSetupWizard() {
33
98
  message: 'API Key:',
34
99
  validate: (input) => input.length > 0 || 'API key is required',
35
100
  },
101
+ ]);
102
+ collectedKeys.set('openai-compatible', answers.apiKey);
103
+ return answers;
104
+ }
105
+ // ── Reasoning model setup ──────────────────────────────────────────────────
106
+ async function setupReasoningModel(collectedKeys) {
107
+ console.log(chalk.bold('\n● Reasoning Model'));
108
+ console.log(chalk.gray('Used for planning and tool calling.\n'));
109
+ const { provider } = await inquirer.prompt([
36
110
  {
37
- type: 'input',
38
- name: 'model',
39
- message: 'Model name:',
40
- default: 'gpt-oss-120b',
111
+ type: 'list',
112
+ name: 'provider',
113
+ message: 'Select provider:',
114
+ choices: PROVIDER_CHOICES,
41
115
  },
42
116
  ]);
43
- // Detect if Harmony format should be used based on model name
44
- const isKrutrimModel = reasoningAnswers.model.includes('gpt-oss-120b');
45
- const defaultUseHarmony = isKrutrimModel;
46
- console.log(chalk.gray('\nTool Format Configuration'));
47
- console.log(chalk.gray('Different providers use different formats for tool calling.\n'));
48
- const { useHarmonyFormat } = await inquirer.prompt([
117
+ const preset = PROVIDERS[provider];
118
+ if (preset.note) {
119
+ console.log(chalk.yellow(`\n ⚠ ${preset.note}\n`));
120
+ }
121
+ let endpoint = preset.endpoint;
122
+ let apiKey;
123
+ if (provider === 'openai-compatible') {
124
+ const custom = await askEndpointAndKey(collectedKeys);
125
+ endpoint = custom.endpoint;
126
+ apiKey = custom.apiKey;
127
+ }
128
+ else {
129
+ console.log(chalk.gray(` Endpoint: ${endpoint}\n`));
130
+ apiKey = await askApiKey(provider, collectedKeys);
131
+ }
132
+ const { model } = await inquirer.prompt([
49
133
  {
50
- type: 'confirm',
51
- name: 'useHarmonyFormat',
52
- message: 'Use Harmony format for tool calling?',
53
- default: defaultUseHarmony,
54
- when: () => {
55
- // Show this prompt for all models, but provide smart default
56
- console.log(chalk.gray(` Recommended: ${defaultUseHarmony ? 'Yes' : 'No'} (${isKrutrimModel ? 'Krutrim uses Harmony format' : 'Standard OpenAI format'})`));
57
- return true;
58
- },
134
+ type: 'input',
135
+ name: 'model',
136
+ message: 'Model name:',
137
+ default: preset.reasoningModel || undefined,
59
138
  },
60
139
  ]);
61
- configManager.setReasoningModel({
140
+ const config = {
62
141
  name: 'reasoning',
63
- endpoint: reasoningAnswers.endpoint,
64
- apiKey: reasoningAnswers.apiKey,
142
+ endpoint,
143
+ apiKey,
65
144
  type: 'reasoning',
66
- defaultModel: reasoningAnswers.model,
67
- useHarmonyFormat,
68
- });
69
- logger.success('Reasoning model configured');
70
- // Multimodal Model Configuration (Optional)
71
- console.log(chalk.bold('\nMultimodal Model Configuration (Optional)'));
72
- console.log(chalk.gray('This model will be used for understanding images.\n'));
73
- const { configureMultimodal } = await inquirer.prompt([
145
+ defaultModel: model,
146
+ useHarmonyFormat: preset.useHarmonyFormat,
147
+ reasoningEffortStrategy: preset.reasoningEffortStrategy,
148
+ ...(preset.defaultMaxTokens ? { defaultMaxTokens: preset.defaultMaxTokens } : {}),
149
+ };
150
+ return { config, provider: provider };
151
+ }
152
+ // ── Multimodal model setup ─────────────────────────────────────────────────
153
+ async function setupMultimodalModel(reasoningProvider, collectedKeys) {
154
+ console.log(chalk.bold('\n● Multimodal Model') + chalk.gray(' (optional)'));
155
+ console.log(chalk.gray('Used for understanding images.\n'));
156
+ if (reasoningProvider === 'sarvam') {
157
+ console.log(chalk.yellow(' ⚠ Sarvam does not have a multimodal model.'));
158
+ console.log(chalk.gray(' Please select a different provider below.\n'));
159
+ }
160
+ const { configure } = await inquirer.prompt([
74
161
  {
75
162
  type: 'confirm',
76
- name: 'configureMultimodal',
77
- message: 'Would you like to configure a multimodal model?',
163
+ name: 'configure',
164
+ message: 'Configure a multimodal model?',
78
165
  default: true,
79
166
  },
80
167
  ]);
81
- if (configureMultimodal) {
82
- const multimodalAnswers = await inquirer.prompt([
83
- {
84
- type: 'input',
85
- name: 'endpoint',
86
- message: 'Multimodal API Endpoint URL:',
87
- default: 'https://cloud.olakrutrim.com/v1/chat/completions',
88
- validate: (input) => {
89
- try {
90
- new URL(input);
91
- return true;
92
- }
93
- catch {
94
- return 'Please enter a valid URL';
95
- }
96
- },
97
- },
98
- {
99
- type: 'password',
100
- name: 'apiKey',
101
- message: 'Multimodal API Key:',
102
- default: reasoningAnswers.apiKey,
103
- validate: (input) => input.length > 0 || 'API key is required',
104
- },
105
- {
106
- type: 'input',
107
- name: 'model',
108
- message: 'Multimodal model name:',
109
- default: 'Llama-4-Maverick-17B-128E-Instruct',
110
- },
111
- ]);
112
- configManager.setMultimodalModel({
113
- name: 'multimodal',
114
- endpoint: multimodalAnswers.endpoint,
115
- apiKey: multimodalAnswers.apiKey,
116
- type: 'multimodal',
117
- defaultModel: multimodalAnswers.model,
118
- });
119
- logger.success('Multimodal model configured');
168
+ if (!configure)
169
+ return null;
170
+ const { provider } = await inquirer.prompt([
171
+ {
172
+ type: 'list',
173
+ name: 'provider',
174
+ message: 'Select provider:',
175
+ choices: MULTIMODAL_PROVIDER_CHOICES,
176
+ default: reasoningProvider !== 'sarvam' ? reasoningProvider : 'groq',
177
+ },
178
+ ]);
179
+ const preset = PROVIDERS[provider];
180
+ let endpoint = preset.endpoint;
181
+ let apiKey;
182
+ if (provider === 'openai-compatible') {
183
+ const custom = await askEndpointAndKey(collectedKeys);
184
+ endpoint = custom.endpoint;
185
+ apiKey = custom.apiKey;
120
186
  }
121
- // ── Tool-Calling Model ────────────────────────────────────────────────────
122
- console.log(chalk.bold('\nTool-Calling Model Configuration (Optional)'));
187
+ else {
188
+ console.log(chalk.gray(` Endpoint: ${endpoint}\n`));
189
+ apiKey = await askApiKey(provider, collectedKeys);
190
+ }
191
+ const { model } = await inquirer.prompt([
192
+ {
193
+ type: 'input',
194
+ name: 'model',
195
+ message: 'Multimodal model name:',
196
+ default: preset.multimodalModel || undefined,
197
+ },
198
+ ]);
199
+ return {
200
+ name: 'multimodal',
201
+ endpoint,
202
+ apiKey,
203
+ type: 'multimodal',
204
+ defaultModel: model,
205
+ };
206
+ }
207
+ // ── Tool-calling model setup ───────────────────────────────────────────────
208
+ async function setupToolCallingModel(reasoningProvider, collectedKeys) {
209
+ console.log(chalk.bold('\n● Tool-Calling Model') + chalk.gray(' (optional)'));
123
210
  console.log(chalk.gray('A dedicated model that reliably formats tool calls as standard JSON.'));
124
- console.log(chalk.gray('When configured it is used as the PRIMARY model for tool execution,'));
125
- console.log(chalk.gray('with the reasoning model acting as a secondary fallback.\n'));
126
- const { configureToolCalling } = await inquirer.prompt([
211
+ console.log(chalk.gray('When configured it is the PRIMARY model for tool execution;\nthe reasoning model is the fallback.\n'));
212
+ const { configure } = await inquirer.prompt([
127
213
  {
128
214
  type: 'confirm',
129
- name: 'configureToolCalling',
130
- message: 'Would you like to configure a tool-calling model?',
215
+ name: 'configure',
216
+ message: 'Configure a dedicated tool-calling model?',
131
217
  default: false,
132
218
  },
133
219
  ]);
134
- if (configureToolCalling) {
135
- const toolCallingAnswers = await inquirer.prompt([
136
- {
137
- type: 'input',
138
- name: 'endpoint',
139
- message: 'Tool-Calling API Endpoint URL:',
140
- default: 'https://cloud.olakrutrim.com/v1/chat/completions',
141
- validate: (input) => {
142
- try {
143
- new URL(input);
144
- return true;
145
- }
146
- catch {
147
- return 'Please enter a valid URL';
148
- }
149
- },
150
- },
151
- {
152
- type: 'password',
153
- name: 'apiKey',
154
- message: 'Tool-Calling API Key:',
155
- default: reasoningAnswers.apiKey,
156
- validate: (input) => input.length > 0 || 'API key is required',
157
- },
158
- {
159
- type: 'input',
160
- name: 'model',
161
- message: 'Tool-Calling model name:',
162
- default: 'gpt-4o-mini',
163
- },
164
- ]);
165
- configManager.setToolCallingModel({
166
- name: 'tool-calling',
167
- endpoint: toolCallingAnswers.endpoint,
168
- apiKey: toolCallingAnswers.apiKey,
169
- type: 'tool-calling',
170
- defaultModel: toolCallingAnswers.model,
171
- useHarmonyFormat: false,
172
- });
220
+ if (!configure)
221
+ return null;
222
+ const { provider } = await inquirer.prompt([
223
+ {
224
+ type: 'list',
225
+ name: 'provider',
226
+ message: 'Select provider:',
227
+ choices: PROVIDER_CHOICES,
228
+ default: reasoningProvider,
229
+ },
230
+ ]);
231
+ const preset = PROVIDERS[provider];
232
+ let endpoint = preset.endpoint;
233
+ let apiKey;
234
+ if (provider === 'openai-compatible') {
235
+ const custom = await askEndpointAndKey(collectedKeys);
236
+ endpoint = custom.endpoint;
237
+ apiKey = custom.apiKey;
238
+ }
239
+ else {
240
+ console.log(chalk.gray(` Endpoint: ${endpoint}\n`));
241
+ apiKey = await askApiKey(provider, collectedKeys);
242
+ }
243
+ const { model } = await inquirer.prompt([
244
+ {
245
+ type: 'input',
246
+ name: 'model',
247
+ message: 'Tool-calling model name:',
248
+ default: preset.toolCallingModel || undefined,
249
+ },
250
+ ]);
251
+ return {
252
+ name: 'tool-calling',
253
+ endpoint,
254
+ apiKey,
255
+ type: 'tool-calling',
256
+ defaultModel: model,
257
+ useHarmonyFormat: false, // always standard format for tool-calling models
258
+ };
259
+ }
260
+ // ── Main wizard ────────────────────────────────────────────────────────────
261
+ export async function runSetupWizard() {
262
+ console.log(chalk.bold.cyan('\n∞ Welcome to Jiva Setup Wizard\n'));
263
+ console.log('This wizard will configure your AI providers.\n');
264
+ console.log(chalk.gray('API keys for the same provider are only asked once.\n'));
265
+ // Track API keys keyed by provider so we never ask twice
266
+ const collectedKeys = new Map();
267
+ // 1. Reasoning model
268
+ const { config: reasoningConfig, provider: reasoningProvider } = await setupReasoningModel(collectedKeys);
269
+ configManager.setReasoningModel(reasoningConfig);
270
+ logger.success('Reasoning model configured');
271
+ // 2. Multimodal model (optional)
272
+ const multimodalConfig = await setupMultimodalModel(reasoningProvider, collectedKeys);
273
+ if (multimodalConfig) {
274
+ configManager.setMultimodalModel(multimodalConfig);
275
+ logger.success('Multimodal model configured');
276
+ }
277
+ // 3. Tool-calling model (optional)
278
+ const toolCallingConfig = await setupToolCallingModel(reasoningProvider, collectedKeys);
279
+ if (toolCallingConfig) {
280
+ configManager.setToolCallingModel(toolCallingConfig);
173
281
  logger.success('Tool-calling model configured');
174
282
  }
175
- // MCP Servers Configuration
176
- console.log(chalk.bold('\nMCP Servers Configuration'));
283
+ // 4. MCP Servers
284
+ console.log(chalk.bold('\n● MCP Servers'));
177
285
  console.log(chalk.gray('Setting up default MCP servers (filesystem, mcp-shell-server)...\n'));
178
286
  configManager.initializeDefaultServers();
179
287
  logger.success('Default MCP servers configured');
180
- // Debug Mode
288
+ // 5. Debug mode
181
289
  const { enableDebug } = await inquirer.prompt([
182
290
  {
183
291
  type: 'confirm',
@@ -192,9 +300,7 @@ export async function runSetupWizard() {
192
300
  console.log('\nYou can now run:', chalk.cyan('jiva'));
193
301
  console.log('');
194
302
  }
195
- /**
196
- * Update existing configuration interactively
197
- */
303
+ // ── Update existing configuration ─────────────────────────────────────────
198
304
  export async function updateConfiguration() {
199
305
  console.log(chalk.bold.cyan('\n🔧 Update Jiva Configuration\n'));
200
306
  const { choice } = await inquirer.prompt([
@@ -214,16 +320,30 @@ export async function updateConfiguration() {
214
320
  ],
215
321
  },
216
322
  ]);
323
+ const collectedKeys = new Map();
217
324
  switch (choice) {
218
- case 'reasoning':
219
- await updateReasoningModel();
325
+ case 'reasoning': {
326
+ const { config } = await setupReasoningModel(collectedKeys);
327
+ configManager.setReasoningModel(config);
328
+ logger.success('Reasoning model updated');
220
329
  break;
221
- case 'multimodal':
222
- await updateMultimodalModel();
330
+ }
331
+ case 'multimodal': {
332
+ const config = await setupMultimodalModel('groq', collectedKeys);
333
+ if (config) {
334
+ configManager.setMultimodalModel(config);
335
+ logger.success('Multimodal model updated');
336
+ }
223
337
  break;
224
- case 'tool-calling':
225
- await updateToolCallingModel();
338
+ }
339
+ case 'tool-calling': {
340
+ const config = await setupToolCallingModel('groq', collectedKeys);
341
+ if (config) {
342
+ configManager.setToolCallingModel(config);
343
+ logger.success('Tool-calling model updated');
344
+ }
226
345
  break;
346
+ }
227
347
  case 'mcp':
228
348
  await manageMCPServers();
229
349
  break;
@@ -241,121 +361,7 @@ export async function updateConfiguration() {
241
361
  break;
242
362
  }
243
363
  }
244
- async function updateReasoningModel() {
245
- const current = configManager.getReasoningModel();
246
- const answers = await inquirer.prompt([
247
- {
248
- type: 'input',
249
- name: 'endpoint',
250
- message: 'API Endpoint URL:',
251
- default: current?.endpoint,
252
- },
253
- {
254
- type: 'password',
255
- name: 'apiKey',
256
- message: 'API Key:',
257
- default: current?.apiKey,
258
- },
259
- {
260
- type: 'input',
261
- name: 'model',
262
- message: 'Model name:',
263
- default: current?.defaultModel,
264
- },
265
- ]);
266
- // Detect if Harmony format should be used based on model name
267
- const isKrutrimModel = answers.model.includes('gpt-oss-120b');
268
- const defaultUseHarmony = isKrutrimModel;
269
- console.log(chalk.gray('\nTool Format Configuration'));
270
- console.log(chalk.gray('Different providers use different formats for tool calling.\n'));
271
- const { useHarmonyFormat } = await inquirer.prompt([
272
- {
273
- type: 'confirm',
274
- name: 'useHarmonyFormat',
275
- message: 'Use Harmony format for tool calling?',
276
- default: current?.useHarmonyFormat ?? defaultUseHarmony,
277
- when: () => {
278
- // Show this prompt for all models, but provide smart default
279
- console.log(chalk.gray(` Recommended: ${defaultUseHarmony ? 'Yes' : 'No'} (${isKrutrimModel ? 'Krutrim uses Harmony format' : 'Standard OpenAI format'})`));
280
- return true;
281
- },
282
- },
283
- ]);
284
- configManager.setReasoningModel({
285
- name: 'reasoning',
286
- endpoint: answers.endpoint,
287
- apiKey: answers.apiKey,
288
- type: 'reasoning',
289
- defaultModel: answers.model,
290
- useHarmonyFormat,
291
- });
292
- logger.success('Reasoning model updated');
293
- }
294
- async function updateMultimodalModel() {
295
- const current = configManager.getMultimodalModel();
296
- const answers = await inquirer.prompt([
297
- {
298
- type: 'input',
299
- name: 'endpoint',
300
- message: 'Multimodal API Endpoint URL:',
301
- default: current?.endpoint || 'https://cloud.olakrutrim.com/v1/chat/completions',
302
- },
303
- {
304
- type: 'password',
305
- name: 'apiKey',
306
- message: 'Multimodal API Key:',
307
- default: current?.apiKey,
308
- },
309
- {
310
- type: 'input',
311
- name: 'model',
312
- message: 'Multimodal model name:',
313
- default: current?.defaultModel || 'Llama-4-Maverick-17B-128E-Instruct',
314
- },
315
- ]);
316
- configManager.setMultimodalModel({
317
- name: 'multimodal',
318
- endpoint: answers.endpoint,
319
- apiKey: answers.apiKey,
320
- type: 'multimodal',
321
- defaultModel: answers.model,
322
- });
323
- logger.success('Multimodal model updated');
324
- }
325
- async function updateToolCallingModel() {
326
- const current = configManager.getToolCallingModel();
327
- console.log(chalk.gray('\nA dedicated tool-calling model is used as the PRIMARY model for tool'));
328
- console.log(chalk.gray('execution. The reasoning model acts as the secondary fallback.\n'));
329
- const answers = await inquirer.prompt([
330
- {
331
- type: 'input',
332
- name: 'endpoint',
333
- message: 'Tool-Calling API Endpoint URL:',
334
- default: current?.endpoint || 'https://cloud.olakrutrim.com/v1/chat/completions',
335
- },
336
- {
337
- type: 'password',
338
- name: 'apiKey',
339
- message: 'Tool-Calling API Key:',
340
- default: current?.apiKey,
341
- },
342
- {
343
- type: 'input',
344
- name: 'model',
345
- message: 'Tool-Calling model name:',
346
- default: current?.defaultModel || 'gpt-4o-mini',
347
- },
348
- ]);
349
- configManager.setToolCallingModel({
350
- name: 'tool-calling',
351
- endpoint: answers.endpoint,
352
- apiKey: answers.apiKey,
353
- type: 'tool-calling',
354
- defaultModel: answers.model,
355
- useHarmonyFormat: false,
356
- });
357
- logger.success('Tool-calling model updated');
358
- }
364
+ // ── Sub-functions (MCP, debug, view, reset) ────────────────────────────────
359
365
  async function manageMCPServers() {
360
366
  const servers = configManager.getMCPServers();
361
367
  const serverNames = Object.keys(servers);
@@ -380,21 +386,9 @@ async function manageMCPServers() {
380
386
  }
381
387
  else if (action === 'add') {
382
388
  const answers = await inquirer.prompt([
383
- {
384
- type: 'input',
385
- name: 'name',
386
- message: 'Server name:',
387
- },
388
- {
389
- type: 'input',
390
- name: 'command',
391
- message: 'Command:',
392
- },
393
- {
394
- type: 'input',
395
- name: 'args',
396
- message: 'Arguments (space-separated):',
397
- },
389
+ { type: 'input', name: 'name', message: 'Server name:' },
390
+ { type: 'input', name: 'command', message: 'Command:' },
391
+ { type: 'input', name: 'args', message: 'Arguments (space-separated):' },
398
392
  ]);
399
393
  configManager.addMCPServer(answers.name, {
400
394
  command: answers.command,
@@ -405,43 +399,26 @@ async function manageMCPServers() {
405
399
  }
406
400
  else if (action === 'remove' && serverNames.length > 0) {
407
401
  const { serverName } = await inquirer.prompt([
408
- {
409
- type: 'list',
410
- name: 'serverName',
411
- message: 'Select server to remove:',
412
- choices: serverNames,
413
- },
402
+ { type: 'list', name: 'serverName', message: 'Select server to remove:', choices: serverNames },
414
403
  ]);
415
404
  configManager.removeMCPServer(serverName);
416
405
  logger.success(`MCP server '${serverName}' removed`);
417
406
  }
418
407
  }
419
408
  async function toggleDebugMode() {
420
- const current = configManager.isDebug();
421
409
  const { enabled } = await inquirer.prompt([
422
- {
423
- type: 'confirm',
424
- name: 'enabled',
425
- message: 'Enable debug mode?',
426
- default: current,
427
- },
410
+ { type: 'confirm', name: 'enabled', message: 'Enable debug mode?', default: configManager.isDebug() },
428
411
  ]);
429
412
  configManager.setDebug(enabled);
430
413
  logger.success(`Debug mode ${enabled ? 'enabled' : 'disabled'}`);
431
414
  }
432
415
  function viewConfiguration() {
433
- const config = configManager.getConfig();
434
416
  console.log('\nCurrent Configuration:');
435
- console.log(JSON.stringify(config, null, 2));
417
+ console.log(JSON.stringify(configManager.getConfig(), null, 2));
436
418
  }
437
419
  async function resetConfiguration() {
438
420
  const { confirm } = await inquirer.prompt([
439
- {
440
- type: 'confirm',
441
- name: 'confirm',
442
- message: chalk.red('Are you sure you want to reset all configuration?'),
443
- default: false,
444
- },
421
+ { type: 'confirm', name: 'confirm', message: chalk.red('Are you sure you want to reset all configuration?'), default: false },
445
422
  ]);
446
423
  if (confirm) {
447
424
  configManager.reset();