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.
- package/README.md +771 -738
- package/docs/API.md +10 -1
- package/docs/PROVIDERS.md +8 -4
- package/package.json +12 -12
- package/src/async/asyncJobStore.js +82 -52
- package/src/async/eventBus.js +25 -20
- package/src/async/fileCache.js +121 -40
- package/src/async/jobRunner.js +65 -39
- package/src/async/providerStreamNormalizer.js +203 -117
- package/src/config.js +374 -102
- package/src/continuationStore.js +32 -24
- package/src/index.js +45 -25
- package/src/prompts/helpPrompt.js +328 -305
- package/src/providers/anthropic.js +303 -119
- package/src/providers/codex.js +103 -45
- package/src/providers/deepseek.js +24 -8
- package/src/providers/google.js +337 -93
- package/src/providers/index.js +1 -1
- package/src/providers/interface.js +16 -11
- package/src/providers/mistral.js +179 -69
- package/src/providers/openai-compatible.js +231 -94
- package/src/providers/openai.js +1094 -914
- package/src/providers/openrouter-endpoints-client.js +220 -216
- package/src/providers/openrouter.js +426 -381
- package/src/providers/xai.js +153 -56
- package/src/resources/helpResource.js +70 -67
- package/src/router.js +95 -67
- package/src/services/summarizationService.js +51 -24
- package/src/systemPrompts.js +89 -89
- package/src/tools/cancelJob.js +31 -19
- package/src/tools/chat.js +997 -883
- package/src/tools/checkStatus.js +86 -65
- package/src/tools/consensus.js +400 -234
- package/src/tools/index.js +39 -16
- package/src/transport/httpTransport.js +82 -55
- package/src/utils/contextProcessor.js +54 -37
- package/src/utils/errorHandler.js +95 -45
- package/src/utils/fileValidator.js +107 -98
- package/src/utils/formatStatus.js +122 -64
- package/src/utils/logger.js +459 -449
- package/src/utils/pathUtils.js +2 -2
- package/src/utils/tokenLimiter.js +216 -216
package/docs/API.md
CHANGED
|
@@ -99,6 +99,12 @@ MCP_TRANSPORT=stdio npm start
|
|
|
99
99
|
"default": false,
|
|
100
100
|
"description": "Enable web search for current information. Example: true for framework docs, false for private code analysis"
|
|
101
101
|
},
|
|
102
|
+
"media_resolution": {
|
|
103
|
+
"type": "string",
|
|
104
|
+
"enum": ["MEDIA_RESOLUTION_LOW", "MEDIA_RESOLUTION_MEDIUM", "MEDIA_RESOLUTION_HIGH", "MEDIA_RESOLUTION_UNSPECIFIED"],
|
|
105
|
+
"default": "MEDIA_RESOLUTION_HIGH",
|
|
106
|
+
"description": "Control image/PDF/video processing quality (Gemini 3.0). Defaults to 'MEDIA_RESOLUTION_HIGH' for Gemini 3.0. Examples: 'MEDIA_RESOLUTION_LOW' (faster, less detail), 'MEDIA_RESOLUTION_MEDIUM' (balanced), 'MEDIA_RESOLUTION_HIGH' (maximum detail)"
|
|
107
|
+
},
|
|
102
108
|
"async": {
|
|
103
109
|
"type": "boolean",
|
|
104
110
|
"default": false,
|
|
@@ -329,10 +335,13 @@ MCP_TRANSPORT=stdio npm start
|
|
|
329
335
|
|
|
330
336
|
| Model | Alias | Context | Tokens | Features | Use Cases |
|
|
331
337
|
|-------|-------|---------|--------|----------|-----------|
|
|
338
|
+
| `gemini-3-pro-preview` | `pro`, `gemini` | 1M | 64K | Thinking levels, enhanced reasoning | Complex problems, deep analysis |
|
|
332
339
|
| `gemini-2.5-flash` | `flash` | 1M | 65K | Ultra-fast | Quick analysis, simple queries |
|
|
333
|
-
| `gemini-2.5-pro` | `pro` | 1M | 65K | Thinking mode | Deep reasoning, architecture |
|
|
340
|
+
| `gemini-2.5-pro` | `pro 2.5` | 1M | 65K | Thinking mode | Deep reasoning, architecture |
|
|
334
341
|
| `gemini-2.0-flash` | `flash2` | 1M | 65K | Latest | Experimental thinking |
|
|
335
342
|
|
|
343
|
+
**Note:** Default aliases `gemini`, `pro`, and `gemini-pro` now point to Gemini 3.0 Pro. Use `gemini-2.5-pro` explicitly if you need the 2.5 version.
|
|
344
|
+
|
|
336
345
|
### X.AI/Grok Models
|
|
337
346
|
|
|
338
347
|
| Model | Alias | Context | Tokens | Features | Use Cases |
|
package/docs/PROVIDERS.md
CHANGED
|
@@ -20,9 +20,11 @@ This guide documents all supported AI providers in the Converse MCP Server and t
|
|
|
20
20
|
- **Get Key**: [makersuite.google.com/app/apikey](https://makersuite.google.com/app/apikey)
|
|
21
21
|
- **Environment Variable**: `GOOGLE_API_KEY`
|
|
22
22
|
- **Supported Models**:
|
|
23
|
-
- `gemini-
|
|
24
|
-
- `gemini-2.5-
|
|
25
|
-
- `gemini-2.
|
|
23
|
+
- `gemini-3-pro-preview` (aliases: `pro`, `gemini`) - Enhanced reasoning with thinking levels (1M context, 64K output)
|
|
24
|
+
- `gemini-2.5-pro` (alias: `pro 2.5`) - Deep reasoning with thinking budget (1M context, 65K output)
|
|
25
|
+
- `gemini-2.5-flash` (alias: `flash`) - Ultra-fast model with thinking budget (1M context, 65K output)
|
|
26
|
+
- `gemini-2.0-flash`, `gemini-2.0-flash-lite` - Latest generation (1M context, 65K output)
|
|
27
|
+
- **Note**: Default aliases (`gemini`, `pro`, `gemini-pro`) now point to Gemini 3.0 Pro. Use `gemini-2.5-pro` explicitly if you need version 2.5.
|
|
26
28
|
|
|
27
29
|
### X.AI (Grok)
|
|
28
30
|
- **API Key Format**: `xai-...` (starts with `xai-`)
|
|
@@ -176,7 +178,9 @@ All providers support streaming responses for real-time output.
|
|
|
176
178
|
|
|
177
179
|
### Thinking/Reasoning Modes
|
|
178
180
|
- **OpenAI**: O3 series models support `reasoning_effort` parameter
|
|
179
|
-
- **Google**:
|
|
181
|
+
- **Google**:
|
|
182
|
+
- Gemini 3.0 Pro: Thinking levels (low/high) via `reasoning_effort` - always enabled
|
|
183
|
+
- Gemini 2.5 Pro/Flash: Thinking budget (token-based) via `reasoning_effort`
|
|
180
184
|
- **Anthropic**: Claude 4 and 3.7 models support extended thinking with `reasoning_effort`
|
|
181
185
|
- **Codex**: Thread-based agentic reasoning with persistent context
|
|
182
186
|
- **Others**: Standard inference only
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "converse-mcp-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"description": "Converse MCP Server - Converse with other LLMs with chat and consensus tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -40,26 +40,26 @@
|
|
|
40
40
|
".env.example"
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@anthropic-ai/sdk": "^0.
|
|
44
|
-
"@google/genai": "^1.
|
|
43
|
+
"@anthropic-ai/sdk": "^0.70.0",
|
|
44
|
+
"@google/genai": "^1.30.0",
|
|
45
45
|
"@mistralai/mistralai": "^1.10.0",
|
|
46
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
47
|
-
"@openai/codex-sdk": "^0.
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.22.0",
|
|
47
|
+
"@openai/codex-sdk": "^0.58.0",
|
|
48
48
|
"cors": "^2.8.5",
|
|
49
49
|
"dotenv": "^17.2.3",
|
|
50
50
|
"express": "^5.1.0",
|
|
51
51
|
"lru-cache": "^11.2.2",
|
|
52
|
-
"openai": "^6.1
|
|
53
|
-
"p-limit": "^
|
|
54
|
-
"vite": "^7.
|
|
52
|
+
"openai": "^6.9.1",
|
|
53
|
+
"p-limit": "^7.2.0",
|
|
54
|
+
"vite": "^7.2.2"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@vitest/coverage-v8": "^
|
|
57
|
+
"@vitest/coverage-v8": "^4.0.10",
|
|
58
58
|
"cross-env": "^10.1.0",
|
|
59
|
-
"eslint": "^9.
|
|
59
|
+
"eslint": "^9.39.1",
|
|
60
60
|
"prettier": "^3.6.2",
|
|
61
|
-
"rimraf": "^6.0
|
|
62
|
-
"vitest": "^
|
|
61
|
+
"rimraf": "^6.1.0",
|
|
62
|
+
"vitest": "^4.0.10"
|
|
63
63
|
},
|
|
64
64
|
"scripts": {
|
|
65
65
|
"kill-server": "node scripts/kill-server.js",
|
|
@@ -92,7 +92,9 @@ export class AsyncJobStoreInterface {
|
|
|
92
92
|
* @returns {Promise<Array>} Array of all jobs
|
|
93
93
|
*/
|
|
94
94
|
async getAllJobs(_options = {}) {
|
|
95
|
-
throw new Error(
|
|
95
|
+
throw new Error(
|
|
96
|
+
'getAllJobs() method must be implemented by storage backend',
|
|
97
|
+
);
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
/**
|
|
@@ -133,7 +135,9 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
133
135
|
super();
|
|
134
136
|
|
|
135
137
|
// Check environment variable for memory TTL
|
|
136
|
-
const envMemoryTTL = process.env.ASYNC_MEMORY_TTL_MS
|
|
138
|
+
const envMemoryTTL = process.env.ASYNC_MEMORY_TTL_MS
|
|
139
|
+
? parseInt(process.env.ASYNC_MEMORY_TTL_MS, 10)
|
|
140
|
+
: null;
|
|
137
141
|
const ttl = envMemoryTTL || 24 * 60 * 60 * 1000; // Default 24 hours
|
|
138
142
|
|
|
139
143
|
// Configure LRU cache with configurable TTL and 10k job capacity
|
|
@@ -166,7 +170,7 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
166
170
|
if (!tool || !['chat', 'consensus'].includes(tool)) {
|
|
167
171
|
throw new AsyncJobStoreError(
|
|
168
172
|
'Invalid tool: must be "chat" or "consensus"',
|
|
169
|
-
'INVALID_TOOL'
|
|
173
|
+
'INVALID_TOOL',
|
|
170
174
|
);
|
|
171
175
|
}
|
|
172
176
|
|
|
@@ -175,7 +179,7 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
175
179
|
if (!jobId) {
|
|
176
180
|
throw new AsyncJobStoreError(
|
|
177
181
|
'jobId is required in options',
|
|
178
|
-
'MISSING_JOB_ID'
|
|
182
|
+
'MISSING_JOB_ID',
|
|
179
183
|
);
|
|
180
184
|
}
|
|
181
185
|
const now = Date.now();
|
|
@@ -211,19 +215,18 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
211
215
|
|
|
212
216
|
// Log event
|
|
213
217
|
this._addEvent(jobState, 'job_created', {
|
|
214
|
-
tool
|
|
218
|
+
tool,
|
|
215
219
|
});
|
|
216
220
|
|
|
217
221
|
debugLog(`AsyncJobStore: Created job ${jobId} for ${tool}`);
|
|
218
222
|
return jobId;
|
|
219
|
-
|
|
220
223
|
} catch (error) {
|
|
221
224
|
if (error instanceof AsyncJobStoreError) {
|
|
222
225
|
throw error;
|
|
223
226
|
}
|
|
224
227
|
throw new AsyncJobStoreError(
|
|
225
228
|
`Failed to create job: ${error.message}`,
|
|
226
|
-
'CREATION_ERROR'
|
|
229
|
+
'CREATION_ERROR',
|
|
227
230
|
);
|
|
228
231
|
}
|
|
229
232
|
}
|
|
@@ -240,7 +243,7 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
240
243
|
if (!jobId || typeof jobId !== 'string') {
|
|
241
244
|
throw new AsyncJobStoreError(
|
|
242
245
|
'Invalid job ID: must be a non-empty string',
|
|
243
|
-
'INVALID_JOB_ID'
|
|
246
|
+
'INVALID_JOB_ID',
|
|
244
247
|
);
|
|
245
248
|
}
|
|
246
249
|
|
|
@@ -254,14 +257,13 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
254
257
|
|
|
255
258
|
// Return deep copy to prevent external mutations
|
|
256
259
|
return this._deepClone(job);
|
|
257
|
-
|
|
258
260
|
} catch (error) {
|
|
259
261
|
if (error instanceof AsyncJobStoreError) {
|
|
260
262
|
throw error;
|
|
261
263
|
}
|
|
262
264
|
throw new AsyncJobStoreError(
|
|
263
265
|
`Failed to retrieve job: ${error.message}`,
|
|
264
|
-
'RETRIEVAL_ERROR'
|
|
266
|
+
'RETRIEVAL_ERROR',
|
|
265
267
|
);
|
|
266
268
|
}
|
|
267
269
|
}
|
|
@@ -279,14 +281,14 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
279
281
|
if (!jobId || typeof jobId !== 'string') {
|
|
280
282
|
throw new AsyncJobStoreError(
|
|
281
283
|
'Invalid job ID: must be a non-empty string',
|
|
282
|
-
'INVALID_JOB_ID'
|
|
284
|
+
'INVALID_JOB_ID',
|
|
283
285
|
);
|
|
284
286
|
}
|
|
285
287
|
|
|
286
288
|
if (!updates || typeof updates !== 'object') {
|
|
287
289
|
throw new AsyncJobStoreError(
|
|
288
290
|
'Invalid updates: must be an object',
|
|
289
|
-
'INVALID_UPDATES'
|
|
291
|
+
'INVALID_UPDATES',
|
|
290
292
|
);
|
|
291
293
|
}
|
|
292
294
|
|
|
@@ -298,7 +300,10 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
298
300
|
const now = Date.now();
|
|
299
301
|
|
|
300
302
|
// Apply updates
|
|
301
|
-
if (
|
|
303
|
+
if (
|
|
304
|
+
updates.status &&
|
|
305
|
+
Object.values(JOB_STATUS).includes(updates.status)
|
|
306
|
+
) {
|
|
302
307
|
job.status = updates.status;
|
|
303
308
|
|
|
304
309
|
// Set startedAt when status changes to running
|
|
@@ -314,7 +319,10 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
314
319
|
if (updates.providers) {
|
|
315
320
|
// Update provider states
|
|
316
321
|
Object.entries(updates.providers).forEach(([provider, state]) => {
|
|
317
|
-
job.providers.set(provider, {
|
|
322
|
+
job.providers.set(provider, {
|
|
323
|
+
...job.providers.get(provider),
|
|
324
|
+
...state,
|
|
325
|
+
});
|
|
318
326
|
});
|
|
319
327
|
}
|
|
320
328
|
|
|
@@ -336,7 +344,22 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
336
344
|
}
|
|
337
345
|
|
|
338
346
|
// Apply any other updates as direct properties on the job
|
|
339
|
-
const reservedFields = [
|
|
347
|
+
const reservedFields = [
|
|
348
|
+
'status',
|
|
349
|
+
'progress',
|
|
350
|
+
'providers',
|
|
351
|
+
'overall',
|
|
352
|
+
'jobId',
|
|
353
|
+
'sessionId',
|
|
354
|
+
'createdAt',
|
|
355
|
+
'updatedAt',
|
|
356
|
+
'events',
|
|
357
|
+
'seq',
|
|
358
|
+
'accumulated_content',
|
|
359
|
+
'title',
|
|
360
|
+
'final_summary',
|
|
361
|
+
'reasoning_summary',
|
|
362
|
+
];
|
|
340
363
|
Object.entries(updates).forEach(([key, value]) => {
|
|
341
364
|
if (!reservedFields.includes(key)) {
|
|
342
365
|
job[key] = value;
|
|
@@ -350,14 +373,13 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
350
373
|
this._addEvent(job, 'job_updated', updates);
|
|
351
374
|
|
|
352
375
|
return true;
|
|
353
|
-
|
|
354
376
|
} catch (error) {
|
|
355
377
|
if (error instanceof AsyncJobStoreError) {
|
|
356
378
|
throw error;
|
|
357
379
|
}
|
|
358
380
|
throw new AsyncJobStoreError(
|
|
359
381
|
`Failed to update job: ${error.message}`,
|
|
360
|
-
'UPDATE_ERROR'
|
|
382
|
+
'UPDATE_ERROR',
|
|
361
383
|
);
|
|
362
384
|
}
|
|
363
385
|
}
|
|
@@ -392,11 +414,10 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
392
414
|
|
|
393
415
|
debugLog(`AsyncJobStore: Completed job ${jobId}`);
|
|
394
416
|
return true;
|
|
395
|
-
|
|
396
417
|
} catch (error) {
|
|
397
418
|
throw new AsyncJobStoreError(
|
|
398
419
|
`Failed to complete job: ${error.message}`,
|
|
399
|
-
'COMPLETION_ERROR'
|
|
420
|
+
'COMPLETION_ERROR',
|
|
400
421
|
);
|
|
401
422
|
}
|
|
402
423
|
}
|
|
@@ -418,9 +439,10 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
418
439
|
const now = Date.now();
|
|
419
440
|
|
|
420
441
|
// Serialize error information
|
|
421
|
-
const errorInfo =
|
|
422
|
-
|
|
423
|
-
|
|
442
|
+
const errorInfo =
|
|
443
|
+
error instanceof Error
|
|
444
|
+
? { message: error.message, name: error.name, stack: error.stack }
|
|
445
|
+
: error;
|
|
424
446
|
|
|
425
447
|
// Update job state
|
|
426
448
|
job.status = JOB_STATUS.FAILED;
|
|
@@ -435,11 +457,10 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
435
457
|
|
|
436
458
|
debugError(`AsyncJobStore: Failed job ${jobId}:`, errorInfo);
|
|
437
459
|
return true;
|
|
438
|
-
|
|
439
460
|
} catch (err) {
|
|
440
461
|
throw new AsyncJobStoreError(
|
|
441
462
|
`Failed to fail job: ${err.message}`,
|
|
442
|
-
'FAILURE_ERROR'
|
|
463
|
+
'FAILURE_ERROR',
|
|
443
464
|
);
|
|
444
465
|
}
|
|
445
466
|
}
|
|
@@ -453,7 +474,7 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
453
474
|
let totalEvents = 0;
|
|
454
475
|
|
|
455
476
|
// Count jobs by status
|
|
456
|
-
Object.values(JOB_STATUS).forEach(status => {
|
|
477
|
+
Object.values(JOB_STATUS).forEach((status) => {
|
|
457
478
|
statusCounts[status] = 0;
|
|
458
479
|
});
|
|
459
480
|
|
|
@@ -512,26 +533,25 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
512
533
|
*/
|
|
513
534
|
async getAllJobs(options = {}) {
|
|
514
535
|
try {
|
|
515
|
-
|
|
516
536
|
const {
|
|
517
537
|
limit = 50,
|
|
518
538
|
status,
|
|
519
539
|
sortBy = 'updatedAt',
|
|
520
|
-
sortOrder = 'desc'
|
|
540
|
+
sortOrder = 'desc',
|
|
521
541
|
} = options;
|
|
522
542
|
|
|
523
543
|
// Validate options
|
|
524
544
|
if (!Number.isInteger(limit) || limit < 1 || limit > 1000) {
|
|
525
545
|
throw new AsyncJobStoreError(
|
|
526
546
|
'Limit must be an integer between 1 and 1000',
|
|
527
|
-
'INVALID_LIMIT'
|
|
547
|
+
'INVALID_LIMIT',
|
|
528
548
|
);
|
|
529
549
|
}
|
|
530
550
|
|
|
531
551
|
if (status && !Object.values(JOB_STATUS).includes(status)) {
|
|
532
552
|
throw new AsyncJobStoreError(
|
|
533
553
|
`Invalid status: must be one of ${Object.values(JOB_STATUS).join(', ')}`,
|
|
534
|
-
'INVALID_STATUS'
|
|
554
|
+
'INVALID_STATUS',
|
|
535
555
|
);
|
|
536
556
|
}
|
|
537
557
|
|
|
@@ -557,14 +577,13 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
557
577
|
|
|
558
578
|
debugLog(`AsyncJobStore: Found ${limitedJobs.length} jobs`);
|
|
559
579
|
return limitedJobs;
|
|
560
|
-
|
|
561
580
|
} catch (error) {
|
|
562
581
|
if (error instanceof AsyncJobStoreError) {
|
|
563
582
|
throw error;
|
|
564
583
|
}
|
|
565
584
|
throw new AsyncJobStoreError(
|
|
566
585
|
`Failed to get all jobs: ${error.message}`,
|
|
567
|
-
'QUERY_ERROR'
|
|
586
|
+
'QUERY_ERROR',
|
|
568
587
|
);
|
|
569
588
|
}
|
|
570
589
|
}
|
|
@@ -584,13 +603,15 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
584
603
|
EVENT_TYPES.JOB_CANCELLED,
|
|
585
604
|
];
|
|
586
605
|
|
|
587
|
-
eventTypes.forEach(eventType => {
|
|
606
|
+
eventTypes.forEach((eventType) => {
|
|
588
607
|
this.eventBus.on(eventType, (eventData) => {
|
|
589
608
|
this._storeEventInJob(eventData);
|
|
590
609
|
});
|
|
591
610
|
});
|
|
592
611
|
|
|
593
|
-
debugLog(
|
|
612
|
+
debugLog(
|
|
613
|
+
'AsyncJobStore: Set up EventBus listeners for job lifecycle events',
|
|
614
|
+
);
|
|
594
615
|
}
|
|
595
616
|
|
|
596
617
|
/**
|
|
@@ -625,9 +646,11 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
625
646
|
|
|
626
647
|
// Update job's last activity
|
|
627
648
|
job.updatedAt = Date.now();
|
|
628
|
-
|
|
629
649
|
} catch (error) {
|
|
630
|
-
debugError(
|
|
650
|
+
debugError(
|
|
651
|
+
'AsyncJobStore: Failed to store EventBus event in job:',
|
|
652
|
+
error,
|
|
653
|
+
);
|
|
631
654
|
}
|
|
632
655
|
}
|
|
633
656
|
|
|
@@ -651,11 +674,11 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
651
674
|
|
|
652
675
|
// Apply filters
|
|
653
676
|
if (options.eventType) {
|
|
654
|
-
events = events.filter(event => event.type === options.eventType);
|
|
677
|
+
events = events.filter((event) => event.type === options.eventType);
|
|
655
678
|
}
|
|
656
679
|
|
|
657
680
|
if (options.afterSeq !== undefined) {
|
|
658
|
-
events = events.filter(event => event.seq > options.afterSeq);
|
|
681
|
+
events = events.filter((event) => event.seq > options.afterSeq);
|
|
659
682
|
}
|
|
660
683
|
|
|
661
684
|
// Apply limit
|
|
@@ -665,9 +688,11 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
665
688
|
|
|
666
689
|
// Return deep copy to prevent mutations
|
|
667
690
|
return this._deepClone(events);
|
|
668
|
-
|
|
669
691
|
} catch (error) {
|
|
670
|
-
debugError(
|
|
692
|
+
debugError(
|
|
693
|
+
`AsyncJobStore: Failed to get events for job ${jobId}:`,
|
|
694
|
+
error,
|
|
695
|
+
);
|
|
671
696
|
return [];
|
|
672
697
|
}
|
|
673
698
|
}
|
|
@@ -686,9 +711,11 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
686
711
|
|
|
687
712
|
const latestEvent = job.events[job.events.length - 1];
|
|
688
713
|
return this._deepClone(latestEvent);
|
|
689
|
-
|
|
690
714
|
} catch (error) {
|
|
691
|
-
debugError(
|
|
715
|
+
debugError(
|
|
716
|
+
`AsyncJobStore: Failed to get latest event for job ${jobId}:`,
|
|
717
|
+
error,
|
|
718
|
+
);
|
|
692
719
|
return null;
|
|
693
720
|
}
|
|
694
721
|
}
|
|
@@ -750,7 +777,7 @@ class LRUAsyncJobStore extends AsyncJobStoreInterface {
|
|
|
750
777
|
}
|
|
751
778
|
|
|
752
779
|
if (Array.isArray(obj)) {
|
|
753
|
-
return obj.map(item => this._deepClone(item));
|
|
780
|
+
return obj.map((item) => this._deepClone(item));
|
|
754
781
|
}
|
|
755
782
|
|
|
756
783
|
const cloned = {};
|
|
@@ -774,16 +801,19 @@ export function getAsyncJobStore() {
|
|
|
774
801
|
asyncJobStore = new LRUAsyncJobStore();
|
|
775
802
|
|
|
776
803
|
// Set up periodic cleanup (runs every 10 minutes, same as continuation store)
|
|
777
|
-
cleanupIntervalId = setInterval(
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
804
|
+
cleanupIntervalId = setInterval(
|
|
805
|
+
async () => {
|
|
806
|
+
try {
|
|
807
|
+
const cleaned = await asyncJobStore.cleanup();
|
|
808
|
+
if (cleaned > 0) {
|
|
809
|
+
debugLog(`AsyncJobStore: Cleaned up ${cleaned} old jobs`);
|
|
810
|
+
}
|
|
811
|
+
} catch (error) {
|
|
812
|
+
debugError('AsyncJobStore cleanup failed:', error);
|
|
782
813
|
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
}, 10 * 60 * 1000);
|
|
814
|
+
},
|
|
815
|
+
10 * 60 * 1000,
|
|
816
|
+
);
|
|
787
817
|
|
|
788
818
|
// Unref the interval so it doesn't keep the process alive
|
|
789
819
|
if (cleanupIntervalId && typeof cleanupIntervalId.unref === 'function') {
|
|
@@ -801,7 +831,7 @@ export function setAsyncJobStore(store) {
|
|
|
801
831
|
if (store !== null && !(store instanceof AsyncJobStoreInterface)) {
|
|
802
832
|
throw new AsyncJobStoreError(
|
|
803
833
|
'Store must extend AsyncJobStoreInterface',
|
|
804
|
-
'INVALID_STORE'
|
|
834
|
+
'INVALID_STORE',
|
|
805
835
|
);
|
|
806
836
|
}
|
|
807
837
|
asyncJobStore = store;
|
package/src/async/eventBus.js
CHANGED
|
@@ -78,7 +78,7 @@ export class EventBus extends EventEmitter {
|
|
|
78
78
|
tool: data.tool,
|
|
79
79
|
options: data.options,
|
|
80
80
|
timestamp: Date.now(),
|
|
81
|
-
...data
|
|
81
|
+
...data,
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -94,7 +94,7 @@ export class EventBus extends EventEmitter {
|
|
|
94
94
|
status: data.status,
|
|
95
95
|
providers: data.providers,
|
|
96
96
|
timestamp: Date.now(),
|
|
97
|
-
...data
|
|
97
|
+
...data,
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -108,7 +108,7 @@ export class EventBus extends EventEmitter {
|
|
|
108
108
|
return this._emitJobEvent(EVENT_TYPES.JOB_COMPLETED, jobId, {
|
|
109
109
|
result,
|
|
110
110
|
timestamp: Date.now(),
|
|
111
|
-
duration: this._calculateDuration(jobId)
|
|
111
|
+
duration: this._calculateDuration(jobId),
|
|
112
112
|
});
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -119,16 +119,19 @@ export class EventBus extends EventEmitter {
|
|
|
119
119
|
* @returns {boolean} True if event was emitted
|
|
120
120
|
*/
|
|
121
121
|
emitJobFailed(jobId, error) {
|
|
122
|
-
const errorData =
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
const errorData =
|
|
123
|
+
error instanceof Error
|
|
124
|
+
? {
|
|
125
|
+
message: error.message,
|
|
126
|
+
code: error.code || 'UNKNOWN_ERROR',
|
|
127
|
+
stack: error.stack,
|
|
128
|
+
}
|
|
129
|
+
: { message: String(error) };
|
|
127
130
|
|
|
128
131
|
return this._emitJobEvent(EVENT_TYPES.JOB_FAILED, jobId, {
|
|
129
132
|
error: errorData,
|
|
130
133
|
timestamp: Date.now(),
|
|
131
|
-
duration: this._calculateDuration(jobId)
|
|
134
|
+
duration: this._calculateDuration(jobId),
|
|
132
135
|
});
|
|
133
136
|
}
|
|
134
137
|
|
|
@@ -143,7 +146,7 @@ export class EventBus extends EventEmitter {
|
|
|
143
146
|
reason: data.reason || 'User cancelled',
|
|
144
147
|
partial_result: data.partial_result,
|
|
145
148
|
timestamp: Date.now(),
|
|
146
|
-
...data
|
|
149
|
+
...data,
|
|
147
150
|
});
|
|
148
151
|
}
|
|
149
152
|
|
|
@@ -157,7 +160,7 @@ export class EventBus extends EventEmitter {
|
|
|
157
160
|
return this._emitJobEvent(EVENT_TYPES.JOB_STARTED, jobId, {
|
|
158
161
|
tool: data.tool,
|
|
159
162
|
timestamp: Date.now(),
|
|
160
|
-
...data
|
|
163
|
+
...data,
|
|
161
164
|
});
|
|
162
165
|
}
|
|
163
166
|
|
|
@@ -171,14 +174,14 @@ export class EventBus extends EventEmitter {
|
|
|
171
174
|
if (!Object.values(EVENT_TYPES).includes(eventType)) {
|
|
172
175
|
throw new EventBusError(
|
|
173
176
|
`Invalid event type: ${eventType}`,
|
|
174
|
-
'INVALID_EVENT_TYPE'
|
|
177
|
+
'INVALID_EVENT_TYPE',
|
|
175
178
|
);
|
|
176
179
|
}
|
|
177
180
|
|
|
178
181
|
if (typeof listener !== 'function') {
|
|
179
182
|
throw new EventBusError(
|
|
180
183
|
'Listener must be a function',
|
|
181
|
-
'INVALID_LISTENER'
|
|
184
|
+
'INVALID_LISTENER',
|
|
182
185
|
);
|
|
183
186
|
}
|
|
184
187
|
|
|
@@ -218,7 +221,7 @@ export class EventBus extends EventEmitter {
|
|
|
218
221
|
...this.stats,
|
|
219
222
|
totalListeners: this.listenerCount(),
|
|
220
223
|
eventHistorySize: this.eventHistory.size,
|
|
221
|
-
memoryUsage: this._calculateMemoryUsage()
|
|
224
|
+
memoryUsage: this._calculateMemoryUsage(),
|
|
222
225
|
};
|
|
223
226
|
}
|
|
224
227
|
|
|
@@ -236,7 +239,7 @@ export class EventBus extends EventEmitter {
|
|
|
236
239
|
if (!jobId || typeof jobId !== 'string') {
|
|
237
240
|
throw new EventBusError(
|
|
238
241
|
'Invalid job ID: must be a non-empty string',
|
|
239
|
-
'INVALID_JOB_ID'
|
|
242
|
+
'INVALID_JOB_ID',
|
|
240
243
|
);
|
|
241
244
|
}
|
|
242
245
|
|
|
@@ -245,7 +248,7 @@ export class EventBus extends EventEmitter {
|
|
|
245
248
|
type: eventType,
|
|
246
249
|
jobId,
|
|
247
250
|
data,
|
|
248
|
-
timestamp: data.timestamp || Date.now()
|
|
251
|
+
timestamp: data.timestamp || Date.now(),
|
|
249
252
|
};
|
|
250
253
|
|
|
251
254
|
// Add to history
|
|
@@ -259,15 +262,17 @@ export class EventBus extends EventEmitter {
|
|
|
259
262
|
|
|
260
263
|
debugLog(`EventBus: Emitted ${eventType} for job ${jobId}`);
|
|
261
264
|
return hasListeners;
|
|
262
|
-
|
|
263
265
|
} catch (error) {
|
|
264
|
-
debugError(
|
|
266
|
+
debugError(
|
|
267
|
+
`EventBus: Failed to emit event ${eventType} for job ${jobId}:`,
|
|
268
|
+
error,
|
|
269
|
+
);
|
|
265
270
|
if (error instanceof EventBusError) {
|
|
266
271
|
throw error;
|
|
267
272
|
}
|
|
268
273
|
throw new EventBusError(
|
|
269
274
|
`Failed to emit event: ${error.message}`,
|
|
270
|
-
'EMISSION_ERROR'
|
|
275
|
+
'EMISSION_ERROR',
|
|
271
276
|
);
|
|
272
277
|
}
|
|
273
278
|
}
|
|
@@ -304,7 +309,7 @@ export class EventBus extends EventEmitter {
|
|
|
304
309
|
return null;
|
|
305
310
|
}
|
|
306
311
|
|
|
307
|
-
const createEvent = history.find(e => e.type === EVENT_TYPES.JOB_CREATED);
|
|
312
|
+
const createEvent = history.find((e) => e.type === EVENT_TYPES.JOB_CREATED);
|
|
308
313
|
if (!createEvent) {
|
|
309
314
|
return null;
|
|
310
315
|
}
|