converse-mcp-server 2.3.1 → 2.4.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 (42) hide show
  1. package/README.md +771 -738
  2. package/docs/API.md +10 -1
  3. package/docs/PROVIDERS.md +8 -4
  4. package/package.json +12 -12
  5. package/src/async/asyncJobStore.js +82 -52
  6. package/src/async/eventBus.js +25 -20
  7. package/src/async/fileCache.js +121 -40
  8. package/src/async/jobRunner.js +65 -39
  9. package/src/async/providerStreamNormalizer.js +203 -117
  10. package/src/config.js +374 -102
  11. package/src/continuationStore.js +32 -24
  12. package/src/index.js +45 -25
  13. package/src/prompts/helpPrompt.js +328 -305
  14. package/src/providers/anthropic.js +303 -119
  15. package/src/providers/codex.js +103 -45
  16. package/src/providers/deepseek.js +24 -8
  17. package/src/providers/google.js +337 -93
  18. package/src/providers/index.js +1 -1
  19. package/src/providers/interface.js +16 -11
  20. package/src/providers/mistral.js +179 -69
  21. package/src/providers/openai-compatible.js +231 -94
  22. package/src/providers/openai.js +1094 -914
  23. package/src/providers/openrouter-endpoints-client.js +220 -216
  24. package/src/providers/openrouter.js +426 -381
  25. package/src/providers/xai.js +153 -56
  26. package/src/resources/helpResource.js +70 -67
  27. package/src/router.js +95 -67
  28. package/src/services/summarizationService.js +51 -24
  29. package/src/systemPrompts.js +89 -89
  30. package/src/tools/cancelJob.js +31 -19
  31. package/src/tools/chat.js +997 -883
  32. package/src/tools/checkStatus.js +86 -65
  33. package/src/tools/consensus.js +400 -234
  34. package/src/tools/index.js +39 -16
  35. package/src/transport/httpTransport.js +82 -55
  36. package/src/utils/contextProcessor.js +54 -37
  37. package/src/utils/errorHandler.js +95 -45
  38. package/src/utils/fileValidator.js +107 -98
  39. package/src/utils/formatStatus.js +122 -64
  40. package/src/utils/logger.js +459 -449
  41. package/src/utils/pathUtils.js +2 -2
  42. package/src/utils/tokenLimiter.js +216 -216
@@ -21,11 +21,16 @@ export async function formatJobListHumanReadable(jobsList, dependencies = {}) {
21
21
 
22
22
  // Summary line - include cancelled jobs if any
23
23
  const summaryParts = [];
24
- if (jobsList.summary.active_jobs > 0) summaryParts.push(`${jobsList.summary.active_jobs} active`);
25
- if (jobsList.summary.completed_jobs > 0) summaryParts.push(`${jobsList.summary.completed_jobs} completed`);
26
- if (jobsList.summary.failed_jobs > 0) summaryParts.push(`${jobsList.summary.failed_jobs} failed`);
27
- if (jobsList.summary.cancelled_jobs > 0) summaryParts.push(`${jobsList.summary.cancelled_jobs} cancelled`);
28
- const summaryStr = summaryParts.length > 0 ? summaryParts.join(', ') : '0 jobs';
24
+ if (jobsList.summary.active_jobs > 0)
25
+ summaryParts.push(`${jobsList.summary.active_jobs} active`);
26
+ if (jobsList.summary.completed_jobs > 0)
27
+ summaryParts.push(`${jobsList.summary.completed_jobs} completed`);
28
+ if (jobsList.summary.failed_jobs > 0)
29
+ summaryParts.push(`${jobsList.summary.failed_jobs} failed`);
30
+ if (jobsList.summary.cancelled_jobs > 0)
31
+ summaryParts.push(`${jobsList.summary.cancelled_jobs} cancelled`);
32
+ const summaryStr =
33
+ summaryParts.length > 0 ? summaryParts.join(', ') : '0 jobs';
29
34
  parts.push(`📊 Jobs Summary: ${summaryStr}`);
30
35
 
31
36
  if (jobsList.jobs.length === 0) {
@@ -37,23 +42,28 @@ export async function formatJobListHumanReadable(jobsList, dependencies = {}) {
37
42
 
38
43
  // List each job
39
44
  for (const job of jobsList.jobs) {
40
- const timeStr = job.elapsed_seconds >= 60
41
- ? `${Math.floor(job.elapsed_seconds / 60)}m${Math.round(job.elapsed_seconds % 60)}s`
42
- : `${job.elapsed_seconds.toFixed(1)}s`;
43
-
44
- const statusEmoji = {
45
- 'queued': '⏳',
46
- 'running': '🔄',
47
- 'completed': '',
48
- 'failed': '',
49
- 'cancelled': '',
50
- 'completed_with_errors': '⚠️'
51
- }[job.status] || '';
52
-
53
- const provider = job.provider || (job.tool === 'consensus' ? 'multiple' : 'unknown');
45
+ const timeStr =
46
+ job.elapsed_seconds >= 60
47
+ ? `${Math.floor(job.elapsed_seconds / 60)}m${Math.round(job.elapsed_seconds % 60)}s`
48
+ : `${job.elapsed_seconds.toFixed(1)}s`;
49
+
50
+ const statusEmoji =
51
+ {
52
+ queued: '',
53
+ running: '🔄',
54
+ completed: '',
55
+ failed: '',
56
+ cancelled: '',
57
+ completed_with_errors: '⚠️',
58
+ }[job.status] || '';
59
+
60
+ const provider =
61
+ job.provider || (job.tool === 'consensus' ? 'multiple' : 'unknown');
54
62
 
55
63
  // Format start time as readable date/time
56
- const startTime = job.created_at ? new Date(job.created_at).toLocaleString() : 'unknown';
64
+ const startTime = job.created_at
65
+ ? new Date(job.created_at).toLocaleString()
66
+ : 'unknown';
57
67
 
58
68
  // Format: emoji STATUS | TOOL | id | sequence | started | time | [progress for consensus only] | provider
59
69
  const sequenceStr = '1/1';
@@ -78,9 +88,10 @@ export async function formatJobListHumanReadable(jobsList, dependencies = {}) {
78
88
  // Add final summary snippet for completed jobs
79
89
  if (job.status === 'completed' && job.final_summary) {
80
90
  // Indent and truncate summary for list view
81
- const summarySnippet = job.final_summary.length > 100
82
- ? job.final_summary.substring(0, 100) + '...'
83
- : job.final_summary;
91
+ const summarySnippet =
92
+ job.final_summary.length > 100
93
+ ? job.final_summary.substring(0, 100) + '...'
94
+ : job.final_summary;
84
95
  parts.push(` └─ ${summarySnippet}`);
85
96
  }
86
97
  }
@@ -97,7 +108,11 @@ export async function formatJobListHumanReadable(jobsList, dependencies = {}) {
97
108
  * @param {object} dependencies - Dependencies object with config and providers
98
109
  * @returns {Promise<string>} Human-readable status text
99
110
  */
100
- export async function formatHumanReadableStatus(jobStatus, options = {}, dependencies = {}) {
111
+ export async function formatHumanReadableStatus(
112
+ jobStatus,
113
+ options = {},
114
+ dependencies = {},
115
+ ) {
101
116
  const parts = [];
102
117
 
103
118
  // Format elapsed time
@@ -111,17 +126,20 @@ export async function formatHumanReadableStatus(jobStatus, options = {}, depende
111
126
  }
112
127
 
113
128
  // Build status line based on status
114
- const statusEmoji = {
115
- 'queued': '⏳',
116
- 'running': '🔄',
117
- 'completed': '',
118
- 'failed': '',
119
- 'cancelled': '',
120
- 'completed_with_errors': '⚠️'
121
- }[jobStatus.status] || '';
129
+ const statusEmoji =
130
+ {
131
+ queued: '',
132
+ running: '🔄',
133
+ completed: '',
134
+ failed: '',
135
+ cancelled: '',
136
+ completed_with_errors: '⚠️',
137
+ }[jobStatus.status] || '❓';
122
138
 
123
139
  // Format start time as readable date/time
124
- const startTime = jobStatus.created_at ? new Date(jobStatus.created_at).toLocaleString() : 'unknown';
140
+ const startTime = jobStatus.created_at
141
+ ? new Date(jobStatus.created_at).toLocaleString()
142
+ : 'unknown';
125
143
 
126
144
  // Add sequence info if provided
127
145
  const sequenceStr = options.sequence ? ` | ${options.sequence}` : '';
@@ -141,8 +159,9 @@ export async function formatHumanReadableStatus(jobStatus, options = {}, depende
141
159
  } else if (jobStatus.providers) {
142
160
  // Calculate progress from provider states
143
161
  const providerEntries = Object.entries(jobStatus.providers);
144
- const completed = providerEntries.filter(([_, state]) =>
145
- state.status === 'completed' || state.status === 'refined'
162
+ const completed = providerEntries.filter(
163
+ ([_, state]) =>
164
+ state.status === 'completed' || state.status === 'refined',
146
165
  ).length;
147
166
  const total = providerEntries.length;
148
167
  statusLine += ` | ${completed}/${total} responded`;
@@ -163,56 +182,83 @@ export async function formatHumanReadableStatus(jobStatus, options = {}, depende
163
182
  parts.push(statusLine);
164
183
 
165
184
  // Show reasoning summary for running jobs from OpenAI reasoning models
166
- if (jobStatus.status === 'running' && !jobStatus.accumulated_content && jobStatus.reasoning_summary) {
167
- debugLog(`[FormatStatus] *** SHOWING REASONING SUMMARY: "${jobStatus.reasoning_summary.substring(0, 100)}..."`);
185
+ if (
186
+ jobStatus.status === 'running' &&
187
+ !jobStatus.accumulated_content &&
188
+ jobStatus.reasoning_summary
189
+ ) {
190
+ debugLog(
191
+ `[FormatStatus] *** SHOWING REASONING SUMMARY: "${jobStatus.reasoning_summary.substring(0, 100)}..."`,
192
+ );
168
193
  parts.push(`Thinking: ${jobStatus.reasoning_summary}`);
169
- } else if (jobStatus.status === 'running' && !jobStatus.accumulated_content && jobStatus.elapsed_seconds > 5) {
194
+ } else if (
195
+ jobStatus.status === 'running' &&
196
+ !jobStatus.accumulated_content &&
197
+ jobStatus.elapsed_seconds > 5
198
+ ) {
170
199
  // Fallback thinking status for jobs without reasoning summaries
171
200
  const thinkingTime = Math.floor(jobStatus.elapsed_seconds);
172
- debugLog('[FormatStatus] *** FALLBACK THINKING (no reasoning_summary available)');
173
- parts.push('Thinking: Model is processing your request (' + thinkingTime + 's elapsed)');
201
+ debugLog(
202
+ '[FormatStatus] *** FALLBACK THINKING (no reasoning_summary available)',
203
+ );
204
+ parts.push(
205
+ 'Thinking: Model is processing your request (' +
206
+ thinkingTime +
207
+ 's elapsed)',
208
+ );
174
209
  }
175
210
 
176
211
  // Generate streaming summary for running jobs if accumulated content available
177
212
  if (jobStatus.status === 'running' && jobStatus.accumulated_content) {
178
213
  try {
179
214
  if (dependencies.config && dependencies.providers) {
180
- const summarizationService = new SummarizationService(dependencies.providers, dependencies.config);
215
+ const summarizationService = new SummarizationService(
216
+ dependencies.providers,
217
+ dependencies.config,
218
+ );
181
219
 
182
220
  // Extract the last 500 characters as the current focus area
183
221
  const contentLength = jobStatus.accumulated_content.length;
184
- const currentFocus = contentLength > 500
185
- ? jobStatus.accumulated_content.substring(contentLength - 500)
186
- : jobStatus.accumulated_content;
222
+ const currentFocus =
223
+ contentLength > 500
224
+ ? jobStatus.accumulated_content.substring(contentLength - 500)
225
+ : jobStatus.accumulated_content;
187
226
 
188
227
  // Generate streaming summary
189
- const streamingSummary = await summarizationService.generateStreamingSummary(
190
- jobStatus.accumulated_content,
191
- currentFocus
192
- );
228
+ const streamingSummary =
229
+ await summarizationService.generateStreamingSummary(
230
+ jobStatus.accumulated_content,
231
+ currentFocus,
232
+ );
193
233
 
194
234
  if (streamingSummary) {
195
235
  parts.push(`Status: ${streamingSummary}`);
196
236
  } else {
197
237
  // Fallback: show truncated accumulated content as streaming preview
198
- const preview = jobStatus.accumulated_content.length > 200
199
- ? jobStatus.accumulated_content.substring(0, 200) + '...'
200
- : jobStatus.accumulated_content;
238
+ const preview =
239
+ jobStatus.accumulated_content.length > 200
240
+ ? jobStatus.accumulated_content.substring(0, 200) + '...'
241
+ : jobStatus.accumulated_content;
201
242
  parts.push(`Streaming: "${preview}"`);
202
243
  }
203
244
  } else {
204
245
  // Fallback when summarization unavailable: show truncated accumulated content
205
- const preview = jobStatus.accumulated_content.length > 200
206
- ? jobStatus.accumulated_content.substring(0, 200) + '...'
207
- : jobStatus.accumulated_content;
246
+ const preview =
247
+ jobStatus.accumulated_content.length > 200
248
+ ? jobStatus.accumulated_content.substring(0, 200) + '...'
249
+ : jobStatus.accumulated_content;
208
250
  parts.push(`Streaming: "${preview}"`);
209
251
  }
210
252
  } catch (error) {
211
- debugError('formatHumanReadableStatus: Failed to generate streaming summary', error);
253
+ debugError(
254
+ 'formatHumanReadableStatus: Failed to generate streaming summary',
255
+ error,
256
+ );
212
257
  // Fall back to showing truncated accumulated content
213
- const preview = jobStatus.accumulated_content.length > 200
214
- ? jobStatus.accumulated_content.substring(0, 200) + '...'
215
- : jobStatus.accumulated_content;
258
+ const preview =
259
+ jobStatus.accumulated_content.length > 200
260
+ ? jobStatus.accumulated_content.substring(0, 200) + '...'
261
+ : jobStatus.accumulated_content;
216
262
  parts.push(`Streaming: "${preview}"`);
217
263
  }
218
264
  }
@@ -225,7 +271,10 @@ export async function formatHumanReadableStatus(jobStatus, options = {}, depende
225
271
  // Optionally show first provider's preview
226
272
  const firstPreview = Object.values(jobStatus.provider_previews)[0];
227
273
  if (firstPreview) {
228
- const truncated = firstPreview.length > 80 ? firstPreview.substring(0, 80) + '...' : firstPreview;
274
+ const truncated =
275
+ firstPreview.length > 80
276
+ ? firstPreview.substring(0, 80) + '...'
277
+ : firstPreview;
229
278
  parts.push(`Preview: "${truncated}"`);
230
279
  }
231
280
  }
@@ -293,13 +342,14 @@ export function formatJobStatus(job, options = {}) {
293
342
  accumulated_content: job.accumulated_content || null,
294
343
  title: job.title || null,
295
344
  final_summary: job.final_summary || null,
296
- reasoning_summary: job.reasoning_summary || null
345
+ reasoning_summary: job.reasoning_summary || null,
297
346
  };
298
347
 
299
348
  // For consensus, gather provider previews
300
349
  if (job.tool === 'consensus') {
301
350
  const providerPreviews = {};
302
- for (let i = 0; i < 10; i++) { // Check up to 10 providers
351
+ for (let i = 0; i < 10; i++) {
352
+ // Check up to 10 providers
303
353
  if (job[`provider_${i}_preview`]) {
304
354
  providerPreviews[`provider_${i}`] = job[`provider_${i}_preview`];
305
355
  }
@@ -321,7 +371,7 @@ export function formatJobStatus(job, options = {}) {
321
371
  formatted.providers[providerId] = {
322
372
  status: providerState.status || 'unknown',
323
373
  progress: providerState.progress || 0,
324
- updated_at: providerState.updatedAt || null
374
+ updated_at: providerState.updatedAt || null,
325
375
  };
326
376
  }
327
377
  }
@@ -346,7 +396,11 @@ export function formatJobStatus(job, options = {}) {
346
396
  * @param {object} dependencies - Dependencies object with config and providers
347
397
  * @returns {Promise<string>} Human-readable conversation history
348
398
  */
349
- export async function formatConversationHistory(jobStatus, continuationId, dependencies = {}) {
399
+ export async function formatConversationHistory(
400
+ jobStatus,
401
+ continuationId,
402
+ dependencies = {},
403
+ ) {
350
404
  const parts = [];
351
405
 
352
406
  parts.push(`📊 Conversation History for ${continuationId}:`);
@@ -355,7 +409,11 @@ export async function formatConversationHistory(jobStatus, continuationId, depen
355
409
 
356
410
  // For now, show the single job with sequence 1/1
357
411
  // TODO: Implement proper conversation tracking when multi-job conversations are supported
358
- const statusLine = await formatHumanReadableStatus(jobStatus, { sequence: '1/1', skipContent: false }, dependencies);
412
+ const statusLine = await formatHumanReadableStatus(
413
+ jobStatus,
414
+ { sequence: '1/1', skipContent: false },
415
+ dependencies,
416
+ );
359
417
  parts.push(statusLine);
360
418
 
361
419
  return parts.join('\n');