@defai.digital/automatosx 12.1.0 → 12.3.0
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/CHANGELOG.md +23 -0
- package/README.md +1 -1
- package/dist/index.js +2223 -1775
- package/dist/mcp/index.js +485 -154
- package/package.json +6 -1
package/dist/mcp/index.js
CHANGED
|
@@ -195,11 +195,7 @@ var init_logger = __esm({
|
|
|
195
195
|
message: entry.message,
|
|
196
196
|
context: entry.context
|
|
197
197
|
});
|
|
198
|
-
|
|
199
|
-
console.error(json);
|
|
200
|
-
} else {
|
|
201
|
-
console.log(json);
|
|
202
|
-
}
|
|
198
|
+
console.error(json);
|
|
203
199
|
return;
|
|
204
200
|
}
|
|
205
201
|
const timestamp = entry.timestamp.toISOString();
|
|
@@ -5156,58 +5152,6 @@ var PRECOMPILED_CONFIG = {
|
|
|
5156
5152
|
"window": "daily",
|
|
5157
5153
|
"resetHourUtc": 0
|
|
5158
5154
|
}
|
|
5159
|
-
},
|
|
5160
|
-
"glm": {
|
|
5161
|
-
"enabled": true,
|
|
5162
|
-
"priority": 4,
|
|
5163
|
-
"timeout": 12e4,
|
|
5164
|
-
"command": "ax-glm",
|
|
5165
|
-
"model": "glm-4",
|
|
5166
|
-
"healthCheck": {
|
|
5167
|
-
"enabled": true,
|
|
5168
|
-
"interval": 3e5,
|
|
5169
|
-
"timeout": 5e3
|
|
5170
|
-
},
|
|
5171
|
-
"circuitBreaker": {
|
|
5172
|
-
"enabled": true,
|
|
5173
|
-
"failureThreshold": 3,
|
|
5174
|
-
"recoveryTimeout": 6e4
|
|
5175
|
-
},
|
|
5176
|
-
"processManagement": {
|
|
5177
|
-
"gracefulShutdownTimeout": 5e3,
|
|
5178
|
-
"forceKillDelay": 1e3
|
|
5179
|
-
},
|
|
5180
|
-
"versionDetection": {
|
|
5181
|
-
"timeout": 5e3,
|
|
5182
|
-
"forceKillDelay": 1e3,
|
|
5183
|
-
"cacheEnabled": true
|
|
5184
|
-
}
|
|
5185
|
-
},
|
|
5186
|
-
"grok": {
|
|
5187
|
-
"enabled": true,
|
|
5188
|
-
"priority": 5,
|
|
5189
|
-
"timeout": 12e4,
|
|
5190
|
-
"command": "ax-grok",
|
|
5191
|
-
"model": "grok-3",
|
|
5192
|
-
"healthCheck": {
|
|
5193
|
-
"enabled": true,
|
|
5194
|
-
"interval": 3e5,
|
|
5195
|
-
"timeout": 5e3
|
|
5196
|
-
},
|
|
5197
|
-
"circuitBreaker": {
|
|
5198
|
-
"enabled": true,
|
|
5199
|
-
"failureThreshold": 3,
|
|
5200
|
-
"recoveryTimeout": 6e4
|
|
5201
|
-
},
|
|
5202
|
-
"processManagement": {
|
|
5203
|
-
"gracefulShutdownTimeout": 5e3,
|
|
5204
|
-
"forceKillDelay": 1e3
|
|
5205
|
-
},
|
|
5206
|
-
"versionDetection": {
|
|
5207
|
-
"timeout": 5e3,
|
|
5208
|
-
"forceKillDelay": 1e3,
|
|
5209
|
-
"cacheEnabled": true
|
|
5210
|
-
}
|
|
5211
5155
|
}
|
|
5212
5156
|
},
|
|
5213
5157
|
"execution": {
|
|
@@ -5419,15 +5363,7 @@ var PRECOMPILED_CONFIG = {
|
|
|
5419
5363
|
"enableFreeTierPrioritization": true,
|
|
5420
5364
|
"enableWorkloadAwareRouting": true
|
|
5421
5365
|
},
|
|
5422
|
-
"
|
|
5423
|
-
"sdkFirstMode": false,
|
|
5424
|
-
"mcpBidirectional": false,
|
|
5425
|
-
"autoInjectMCPConfig": false,
|
|
5426
|
-
"sdkFallbackEnabled": true,
|
|
5427
|
-
"deprecationWarnings": true,
|
|
5428
|
-
"providerMetrics": true
|
|
5429
|
-
},
|
|
5430
|
-
"version": "12.1.0"
|
|
5366
|
+
"version": "12.3.0"
|
|
5431
5367
|
};
|
|
5432
5368
|
|
|
5433
5369
|
// src/core/config/schemas.ts
|
|
@@ -14213,6 +14149,7 @@ var ConversationContextStore = class {
|
|
|
14213
14149
|
storePath;
|
|
14214
14150
|
maxEntries;
|
|
14215
14151
|
ttlMs;
|
|
14152
|
+
destroyed = false;
|
|
14216
14153
|
constructor(options) {
|
|
14217
14154
|
this.storePath = options.storePath;
|
|
14218
14155
|
this.maxEntries = options.maxEntries ?? 100;
|
|
@@ -14249,6 +14186,9 @@ var ConversationContextStore = class {
|
|
|
14249
14186
|
* Initialize store (create directory if needed)
|
|
14250
14187
|
*/
|
|
14251
14188
|
async initialize() {
|
|
14189
|
+
if (this.destroyed) {
|
|
14190
|
+
throw new Error("Context store is destroyed");
|
|
14191
|
+
}
|
|
14252
14192
|
try {
|
|
14253
14193
|
await mkdir(this.storePath, { recursive: true });
|
|
14254
14194
|
logger.info("[ContextStore] Initialized", { storePath: this.storePath });
|
|
@@ -14261,6 +14201,9 @@ var ConversationContextStore = class {
|
|
|
14261
14201
|
* Save conversation context
|
|
14262
14202
|
*/
|
|
14263
14203
|
async save(context) {
|
|
14204
|
+
if (this.destroyed) {
|
|
14205
|
+
throw new Error("Context store is destroyed");
|
|
14206
|
+
}
|
|
14264
14207
|
await this.initialize();
|
|
14265
14208
|
const filePath = this.validateAndResolvePath(context.id);
|
|
14266
14209
|
const data = JSON.stringify(context, null, 2);
|
|
@@ -14304,6 +14247,9 @@ var ConversationContextStore = class {
|
|
|
14304
14247
|
* List all context entries (non-expired)
|
|
14305
14248
|
*/
|
|
14306
14249
|
async list(options) {
|
|
14250
|
+
if (this.destroyed) {
|
|
14251
|
+
return [];
|
|
14252
|
+
}
|
|
14307
14253
|
try {
|
|
14308
14254
|
const files = await readdir(this.storePath);
|
|
14309
14255
|
const contexts = [];
|
|
@@ -14406,6 +14352,9 @@ var ConversationContextStore = class {
|
|
|
14406
14352
|
* Get storage statistics
|
|
14407
14353
|
*/
|
|
14408
14354
|
async stats() {
|
|
14355
|
+
if (this.destroyed) {
|
|
14356
|
+
return { totalEntries: 0, bySource: {} };
|
|
14357
|
+
}
|
|
14409
14358
|
const contexts = await this.list();
|
|
14410
14359
|
const bySource = {};
|
|
14411
14360
|
for (const context of contexts) {
|
|
@@ -14418,6 +14367,12 @@ var ConversationContextStore = class {
|
|
|
14418
14367
|
newestEntry: contexts[0]?.timestamp
|
|
14419
14368
|
};
|
|
14420
14369
|
}
|
|
14370
|
+
/**
|
|
14371
|
+
* Destroy store (prevent further I/O)
|
|
14372
|
+
*/
|
|
14373
|
+
destroy() {
|
|
14374
|
+
this.destroyed = true;
|
|
14375
|
+
}
|
|
14421
14376
|
};
|
|
14422
14377
|
|
|
14423
14378
|
// src/mcp/tools/run-agent.ts
|
|
@@ -17194,7 +17149,7 @@ async function executeViaMcpPool(provider, agent, task, pool) {
|
|
|
17194
17149
|
pool.release(provider, client);
|
|
17195
17150
|
}
|
|
17196
17151
|
}
|
|
17197
|
-
async function executeViaCli(agent, task, actualProvider, no_memory, deps, isFallback) {
|
|
17152
|
+
async function executeViaCli(agent, task, actualProvider, no_memory, deps, isFallback, signal) {
|
|
17198
17153
|
const context = await deps.contextManager.createContext(agent, task, {
|
|
17199
17154
|
provider: actualProvider,
|
|
17200
17155
|
skipMemory: no_memory
|
|
@@ -17203,7 +17158,8 @@ async function executeViaCli(agent, task, actualProvider, no_memory, deps, isFal
|
|
|
17203
17158
|
const startTime = Date.now();
|
|
17204
17159
|
const result = await executor.execute(context, {
|
|
17205
17160
|
showProgress: false,
|
|
17206
|
-
verbose: false
|
|
17161
|
+
verbose: false,
|
|
17162
|
+
signal
|
|
17207
17163
|
});
|
|
17208
17164
|
const latencyMs = Date.now() - startTime;
|
|
17209
17165
|
const executionMode = isFallback ? "cli_fallback" : "cli_spawn";
|
|
@@ -17275,8 +17231,11 @@ async function buildAgentContext(agentName, task, deps, callerProvider, bestProv
|
|
|
17275
17231
|
};
|
|
17276
17232
|
}
|
|
17277
17233
|
function createRunAgentHandler(deps) {
|
|
17278
|
-
return async (input) => {
|
|
17234
|
+
return async (input, context) => {
|
|
17279
17235
|
const { agent, task, provider, no_memory, mode = "auto" } = input;
|
|
17236
|
+
if (context?.signal?.aborted) {
|
|
17237
|
+
throw new Error("Request was cancelled");
|
|
17238
|
+
}
|
|
17280
17239
|
validateAgentName(agent);
|
|
17281
17240
|
validateStringParameter(task, "task", {
|
|
17282
17241
|
required: true,
|
|
@@ -17303,6 +17262,9 @@ function createRunAgentHandler(deps) {
|
|
|
17303
17262
|
bestProvider = selectedProvider?.name || "claude-code";
|
|
17304
17263
|
}
|
|
17305
17264
|
const shouldReturnContext = mode === "context" || mode === "auto" && callerActual === bestProvider && callerProvider !== "unknown";
|
|
17265
|
+
if (context?.signal?.aborted) {
|
|
17266
|
+
throw new Error("Request was cancelled");
|
|
17267
|
+
}
|
|
17306
17268
|
logger.info("[Smart Routing] Decision", {
|
|
17307
17269
|
mode,
|
|
17308
17270
|
callerProvider,
|
|
@@ -17337,6 +17299,7 @@ function createRunAgentHandler(deps) {
|
|
|
17337
17299
|
const shouldTryMcp = !!deps.mcpPool;
|
|
17338
17300
|
if (shouldTryMcp && deps.mcpPool) {
|
|
17339
17301
|
try {
|
|
17302
|
+
if (context?.signal?.aborted) throw new Error("Request was cancelled");
|
|
17340
17303
|
const result = await executeViaMcpPool(
|
|
17341
17304
|
bestProvider,
|
|
17342
17305
|
agent,
|
|
@@ -17366,7 +17329,10 @@ function createRunAgentHandler(deps) {
|
|
|
17366
17329
|
}
|
|
17367
17330
|
}
|
|
17368
17331
|
try {
|
|
17369
|
-
|
|
17332
|
+
if (context?.signal?.aborted) {
|
|
17333
|
+
throw new Error("Request was cancelled");
|
|
17334
|
+
}
|
|
17335
|
+
const result = await executeViaCli(agent, task, actualProvider, no_memory, deps, shouldTryMcp, context?.signal);
|
|
17370
17336
|
logger.info("[MCP] run_agent completed (CLI spawn mode)", {
|
|
17371
17337
|
agent,
|
|
17372
17338
|
latencyMs: result.latencyMs,
|
|
@@ -18634,6 +18600,11 @@ function compressWithInfo(payload, options = {}) {
|
|
|
18634
18600
|
init_esm_shims();
|
|
18635
18601
|
init_factory();
|
|
18636
18602
|
init_logger();
|
|
18603
|
+
var NATIVE_MODULE_ERROR_PATTERNS = [
|
|
18604
|
+
"NODE_MODULE_VERSION",
|
|
18605
|
+
"was compiled against a different Node.js version",
|
|
18606
|
+
"better_sqlite3.node"
|
|
18607
|
+
];
|
|
18637
18608
|
var DEFAULT_CONFIG3 = {
|
|
18638
18609
|
dbPath: ".automatosx/tasks/tasks.db",
|
|
18639
18610
|
maxPayloadBytes: 1024 * 1024,
|
|
@@ -18761,6 +18732,28 @@ var SQL = {
|
|
|
18761
18732
|
SELECT id FROM task_store WHERE payload_hash = ? AND status = 'completed' LIMIT 1
|
|
18762
18733
|
`
|
|
18763
18734
|
};
|
|
18735
|
+
function generateTaskId() {
|
|
18736
|
+
const timestamp = Date.now().toString(36);
|
|
18737
|
+
const random = randomBytes(4).toString("hex");
|
|
18738
|
+
return `task_${timestamp}${random}`;
|
|
18739
|
+
}
|
|
18740
|
+
function hashPayload(json) {
|
|
18741
|
+
return createHash("sha256").update(json).digest("hex").substring(0, 16);
|
|
18742
|
+
}
|
|
18743
|
+
function estimateEngine(type) {
|
|
18744
|
+
switch (type) {
|
|
18745
|
+
case "web_search":
|
|
18746
|
+
return "gemini";
|
|
18747
|
+
case "code_review":
|
|
18748
|
+
case "code_generation":
|
|
18749
|
+
return "claude";
|
|
18750
|
+
case "analysis":
|
|
18751
|
+
return "claude";
|
|
18752
|
+
case "custom":
|
|
18753
|
+
default:
|
|
18754
|
+
return "auto";
|
|
18755
|
+
}
|
|
18756
|
+
}
|
|
18764
18757
|
var TaskStore = class {
|
|
18765
18758
|
db;
|
|
18766
18759
|
config;
|
|
@@ -18821,15 +18814,15 @@ var TaskStore = class {
|
|
|
18821
18814
|
isCompressed = false;
|
|
18822
18815
|
compressionRatio = 1;
|
|
18823
18816
|
}
|
|
18824
|
-
const taskId =
|
|
18825
|
-
const payloadHash =
|
|
18817
|
+
const taskId = generateTaskId();
|
|
18818
|
+
const payloadHash = hashPayload(payloadJson);
|
|
18826
18819
|
const now = Date.now();
|
|
18827
18820
|
const ttlHours = Math.min(
|
|
18828
18821
|
validated.ttlHours ?? this.config.defaultTtlHours,
|
|
18829
18822
|
this.config.maxTtlHours
|
|
18830
18823
|
);
|
|
18831
18824
|
const expiresAt = now + ttlHours * 60 * 60 * 1e3;
|
|
18832
|
-
const estimatedEngine = validated.engine === "auto" ?
|
|
18825
|
+
const estimatedEngine = validated.engine === "auto" ? estimateEngine(validated.type) : validated.engine;
|
|
18833
18826
|
try {
|
|
18834
18827
|
this.stmtInsert.run({
|
|
18835
18828
|
id: taskId,
|
|
@@ -19172,31 +19165,6 @@ var TaskStore = class {
|
|
|
19172
19165
|
throw new TaskEngineError("TaskStore is closed", "STORE_ERROR");
|
|
19173
19166
|
}
|
|
19174
19167
|
}
|
|
19175
|
-
generateTaskId() {
|
|
19176
|
-
const timestamp = Date.now().toString(36);
|
|
19177
|
-
const random = randomBytes(4).toString("hex");
|
|
19178
|
-
return `task_${timestamp}${random}`;
|
|
19179
|
-
}
|
|
19180
|
-
hashPayload(json) {
|
|
19181
|
-
return createHash("sha256").update(json).digest("hex").substring(0, 16);
|
|
19182
|
-
}
|
|
19183
|
-
estimateEngine(type) {
|
|
19184
|
-
switch (type) {
|
|
19185
|
-
case "web_search":
|
|
19186
|
-
return "gemini";
|
|
19187
|
-
// Gemini is good for web search
|
|
19188
|
-
case "code_review":
|
|
19189
|
-
case "code_generation":
|
|
19190
|
-
return "claude";
|
|
19191
|
-
// Claude is good for code
|
|
19192
|
-
case "analysis":
|
|
19193
|
-
return "claude";
|
|
19194
|
-
// Claude is good for analysis
|
|
19195
|
-
case "custom":
|
|
19196
|
-
default:
|
|
19197
|
-
return "auto";
|
|
19198
|
-
}
|
|
19199
|
-
}
|
|
19200
19168
|
rowToTask(row) {
|
|
19201
19169
|
let payload;
|
|
19202
19170
|
try {
|
|
@@ -19263,8 +19231,248 @@ var TaskStore = class {
|
|
|
19263
19231
|
};
|
|
19264
19232
|
}
|
|
19265
19233
|
};
|
|
19234
|
+
var InMemoryTaskStore = class {
|
|
19235
|
+
tasks = /* @__PURE__ */ new Map();
|
|
19236
|
+
config;
|
|
19237
|
+
closed = false;
|
|
19238
|
+
constructor(config = {}) {
|
|
19239
|
+
this.config = { ...DEFAULT_CONFIG3, ...config };
|
|
19240
|
+
logger.warn("Using in-memory task store fallback (SQLite unavailable)", {
|
|
19241
|
+
nodeVersion: process.version
|
|
19242
|
+
});
|
|
19243
|
+
}
|
|
19244
|
+
createTask(input) {
|
|
19245
|
+
this.ensureOpen();
|
|
19246
|
+
const validated = CreateTaskInputSchema.parse(input);
|
|
19247
|
+
const payloadJson = JSON.stringify(validated.payload);
|
|
19248
|
+
const payloadSize = Buffer.byteLength(payloadJson, "utf-8");
|
|
19249
|
+
if (payloadSize > this.config.maxPayloadBytes) {
|
|
19250
|
+
throw new TaskEngineError(
|
|
19251
|
+
`Payload size ${payloadSize} exceeds limit ${this.config.maxPayloadBytes}`,
|
|
19252
|
+
"PAYLOAD_TOO_LARGE",
|
|
19253
|
+
{ payloadSize, limit: this.config.maxPayloadBytes }
|
|
19254
|
+
);
|
|
19255
|
+
}
|
|
19256
|
+
const id = generateTaskId();
|
|
19257
|
+
const now = Date.now();
|
|
19258
|
+
const ttlHours = Math.min(
|
|
19259
|
+
validated.ttlHours ?? this.config.defaultTtlHours,
|
|
19260
|
+
this.config.maxTtlHours
|
|
19261
|
+
);
|
|
19262
|
+
const expiresAt = now + ttlHours * 60 * 60 * 1e3;
|
|
19263
|
+
const estimatedEngine = validated.engine === "auto" ? estimateEngine(validated.type) : validated.engine;
|
|
19264
|
+
const task = {
|
|
19265
|
+
id,
|
|
19266
|
+
type: validated.type,
|
|
19267
|
+
status: "pending",
|
|
19268
|
+
engine: validated.engine === "auto" ? null : validated.engine,
|
|
19269
|
+
priority: validated.priority ?? 5,
|
|
19270
|
+
payload: validated.payload,
|
|
19271
|
+
payloadSize,
|
|
19272
|
+
result: null,
|
|
19273
|
+
context: {
|
|
19274
|
+
originClient: validated.context?.originClient ?? "unknown",
|
|
19275
|
+
callChain: validated.context?.callChain ?? [],
|
|
19276
|
+
depth: validated.context?.depth ?? 0
|
|
19277
|
+
},
|
|
19278
|
+
createdAt: now,
|
|
19279
|
+
startedAt: null,
|
|
19280
|
+
completedAt: null,
|
|
19281
|
+
expiresAt,
|
|
19282
|
+
metrics: null,
|
|
19283
|
+
error: null,
|
|
19284
|
+
retryCount: 0,
|
|
19285
|
+
payloadHash: hashPayload(payloadJson),
|
|
19286
|
+
compressionRatio: 1
|
|
19287
|
+
};
|
|
19288
|
+
this.tasks.set(id, task);
|
|
19289
|
+
return {
|
|
19290
|
+
id,
|
|
19291
|
+
status: "pending",
|
|
19292
|
+
estimatedEngine: estimatedEngine ?? null,
|
|
19293
|
+
expiresAt,
|
|
19294
|
+
payloadSize,
|
|
19295
|
+
compressionRatio: 1
|
|
19296
|
+
};
|
|
19297
|
+
}
|
|
19298
|
+
getTask(taskId) {
|
|
19299
|
+
this.ensureOpen();
|
|
19300
|
+
const task = this.tasks.get(taskId);
|
|
19301
|
+
return task ? structuredClone(task) : null;
|
|
19302
|
+
}
|
|
19303
|
+
updateTaskStatus(taskId, status, error) {
|
|
19304
|
+
this.ensureOpen();
|
|
19305
|
+
const task = this.tasks.get(taskId);
|
|
19306
|
+
if (!task) {
|
|
19307
|
+
throw new TaskEngineError(`Task not found: ${taskId}`, "TASK_NOT_FOUND");
|
|
19308
|
+
}
|
|
19309
|
+
const now = Date.now();
|
|
19310
|
+
task.status = status;
|
|
19311
|
+
task.startedAt = status === "running" ? now : task.startedAt;
|
|
19312
|
+
if (status === "completed" || status === "failed") {
|
|
19313
|
+
task.completedAt = now;
|
|
19314
|
+
}
|
|
19315
|
+
task.error = error ?? null;
|
|
19316
|
+
}
|
|
19317
|
+
updateTaskResult(taskId, result, metrics) {
|
|
19318
|
+
this.ensureOpen();
|
|
19319
|
+
const task = this.tasks.get(taskId);
|
|
19320
|
+
if (!task) {
|
|
19321
|
+
throw new TaskEngineError(`Task not found: ${taskId}`, "TASK_NOT_FOUND");
|
|
19322
|
+
}
|
|
19323
|
+
task.status = "completed";
|
|
19324
|
+
task.result = result;
|
|
19325
|
+
task.metrics = {
|
|
19326
|
+
durationMs: metrics.durationMs ?? 0,
|
|
19327
|
+
tokensPrompt: metrics.tokensPrompt ?? null,
|
|
19328
|
+
tokensCompletion: metrics.tokensCompletion ?? null
|
|
19329
|
+
};
|
|
19330
|
+
task.completedAt = Date.now();
|
|
19331
|
+
task.error = null;
|
|
19332
|
+
}
|
|
19333
|
+
updateTaskFailed(taskId, error, durationMs) {
|
|
19334
|
+
this.ensureOpen();
|
|
19335
|
+
const task = this.tasks.get(taskId);
|
|
19336
|
+
if (!task) {
|
|
19337
|
+
throw new TaskEngineError(`Task not found: ${taskId}`, "TASK_NOT_FOUND");
|
|
19338
|
+
}
|
|
19339
|
+
task.status = "failed";
|
|
19340
|
+
task.error = error;
|
|
19341
|
+
task.metrics = task.metrics ?? {
|
|
19342
|
+
durationMs: durationMs ?? 0,
|
|
19343
|
+
tokensPrompt: null,
|
|
19344
|
+
tokensCompletion: null
|
|
19345
|
+
};
|
|
19346
|
+
task.completedAt = Date.now();
|
|
19347
|
+
}
|
|
19348
|
+
incrementRetry(taskId) {
|
|
19349
|
+
this.ensureOpen();
|
|
19350
|
+
const task = this.tasks.get(taskId);
|
|
19351
|
+
if (task) {
|
|
19352
|
+
task.retryCount += 1;
|
|
19353
|
+
}
|
|
19354
|
+
}
|
|
19355
|
+
deleteTask(taskId) {
|
|
19356
|
+
this.ensureOpen();
|
|
19357
|
+
return this.tasks.delete(taskId);
|
|
19358
|
+
}
|
|
19359
|
+
listTasks(filter = {}) {
|
|
19360
|
+
this.ensureOpen();
|
|
19361
|
+
const validated = TaskFilterSchema.parse(filter);
|
|
19362
|
+
const filtered = Array.from(this.tasks.values()).filter((task) => {
|
|
19363
|
+
if (validated.status && task.status !== validated.status) return false;
|
|
19364
|
+
if (validated.engine && task.engine !== validated.engine) return false;
|
|
19365
|
+
if (validated.type && task.type !== validated.type) return false;
|
|
19366
|
+
if (validated.originClient && task.context.originClient !== validated.originClient) return false;
|
|
19367
|
+
return true;
|
|
19368
|
+
});
|
|
19369
|
+
filtered.sort((a, b) => {
|
|
19370
|
+
if (a.priority !== b.priority) return b.priority - a.priority;
|
|
19371
|
+
return a.createdAt - b.createdAt;
|
|
19372
|
+
});
|
|
19373
|
+
const start = validated.offset ?? 0;
|
|
19374
|
+
const end = start + (validated.limit ?? 20);
|
|
19375
|
+
return filtered.slice(start, end).map((task) => structuredClone(task));
|
|
19376
|
+
}
|
|
19377
|
+
countTasks(filter = {}) {
|
|
19378
|
+
this.ensureOpen();
|
|
19379
|
+
const validated = TaskFilterSchema.parse(filter);
|
|
19380
|
+
return Array.from(this.tasks.values()).filter((task) => {
|
|
19381
|
+
if (validated.status && task.status !== validated.status) return false;
|
|
19382
|
+
if (validated.engine && task.engine !== validated.engine) return false;
|
|
19383
|
+
if (validated.type && task.type !== validated.type) return false;
|
|
19384
|
+
if (validated.originClient && task.context.originClient !== validated.originClient) return false;
|
|
19385
|
+
return true;
|
|
19386
|
+
}).length;
|
|
19387
|
+
}
|
|
19388
|
+
findByPayloadHash(payloadHash) {
|
|
19389
|
+
this.ensureOpen();
|
|
19390
|
+
for (const task of this.tasks.values()) {
|
|
19391
|
+
if (task.status === "completed" && task.payloadHash === payloadHash) {
|
|
19392
|
+
return task.id;
|
|
19393
|
+
}
|
|
19394
|
+
}
|
|
19395
|
+
return null;
|
|
19396
|
+
}
|
|
19397
|
+
cleanupExpired() {
|
|
19398
|
+
this.ensureOpen();
|
|
19399
|
+
const now = Date.now();
|
|
19400
|
+
let removed = 0;
|
|
19401
|
+
for (const [id, task] of this.tasks) {
|
|
19402
|
+
if (task.expiresAt < now && task.status !== "running") {
|
|
19403
|
+
this.tasks.delete(id);
|
|
19404
|
+
removed++;
|
|
19405
|
+
}
|
|
19406
|
+
}
|
|
19407
|
+
return removed;
|
|
19408
|
+
}
|
|
19409
|
+
cleanupZombieRunning() {
|
|
19410
|
+
this.ensureOpen();
|
|
19411
|
+
const now = Date.now();
|
|
19412
|
+
let converted = 0;
|
|
19413
|
+
for (const task of this.tasks.values()) {
|
|
19414
|
+
if (task.status === "running" && task.expiresAt < now) {
|
|
19415
|
+
task.status = "failed";
|
|
19416
|
+
task.error = {
|
|
19417
|
+
code: "ZOMBIE_TASK",
|
|
19418
|
+
message: "Task was stuck in running state past expiry"
|
|
19419
|
+
};
|
|
19420
|
+
task.completedAt = now;
|
|
19421
|
+
converted++;
|
|
19422
|
+
}
|
|
19423
|
+
}
|
|
19424
|
+
return converted;
|
|
19425
|
+
}
|
|
19426
|
+
cleanupAll() {
|
|
19427
|
+
const zombies = this.cleanupZombieRunning();
|
|
19428
|
+
const expired = this.cleanupExpired();
|
|
19429
|
+
return { expired, zombies };
|
|
19430
|
+
}
|
|
19431
|
+
getStats() {
|
|
19432
|
+
this.ensureOpen();
|
|
19433
|
+
const byStatus = {
|
|
19434
|
+
pending: 0,
|
|
19435
|
+
running: 0,
|
|
19436
|
+
completed: 0,
|
|
19437
|
+
failed: 0,
|
|
19438
|
+
expired: 0
|
|
19439
|
+
};
|
|
19440
|
+
for (const task of this.tasks.values()) {
|
|
19441
|
+
byStatus[task.status]++;
|
|
19442
|
+
}
|
|
19443
|
+
return {
|
|
19444
|
+
totalTasks: this.tasks.size,
|
|
19445
|
+
byStatus,
|
|
19446
|
+
dbSizeBytes: 0
|
|
19447
|
+
};
|
|
19448
|
+
}
|
|
19449
|
+
close() {
|
|
19450
|
+
this.closed = true;
|
|
19451
|
+
}
|
|
19452
|
+
ensureOpen() {
|
|
19453
|
+
if (this.closed) {
|
|
19454
|
+
throw new TaskEngineError("TaskStore is closed", "STORE_ERROR");
|
|
19455
|
+
}
|
|
19456
|
+
}
|
|
19457
|
+
};
|
|
19458
|
+
function isNativeModuleError(error) {
|
|
19459
|
+
if (!(error instanceof Error)) return false;
|
|
19460
|
+
const message = error.message ?? "";
|
|
19461
|
+
return NATIVE_MODULE_ERROR_PATTERNS.some((pattern) => message.includes(pattern));
|
|
19462
|
+
}
|
|
19266
19463
|
function createTaskStore(config) {
|
|
19267
|
-
|
|
19464
|
+
try {
|
|
19465
|
+
return new TaskStore(config);
|
|
19466
|
+
} catch (error) {
|
|
19467
|
+
if (isNativeModuleError(error)) {
|
|
19468
|
+
logger.warn("Falling back to in-memory task store (native SQLite unavailable)", {
|
|
19469
|
+
nodeVersion: process.version,
|
|
19470
|
+
error: error instanceof Error ? error.message : String(error)
|
|
19471
|
+
});
|
|
19472
|
+
return new InMemoryTaskStore(config);
|
|
19473
|
+
}
|
|
19474
|
+
throw error;
|
|
19475
|
+
}
|
|
19268
19476
|
}
|
|
19269
19477
|
|
|
19270
19478
|
// src/core/task-engine/engine.ts
|
|
@@ -19355,6 +19563,9 @@ var TaskEngine = class extends EventEmitter {
|
|
|
19355
19563
|
if (!task) {
|
|
19356
19564
|
throw new TaskEngineError(`Task not found: ${taskId}`, "TASK_NOT_FOUND");
|
|
19357
19565
|
}
|
|
19566
|
+
if (options.abortSignal?.aborted) {
|
|
19567
|
+
throw new TaskEngineError("Task execution aborted", "EXECUTION_TIMEOUT");
|
|
19568
|
+
}
|
|
19358
19569
|
if (task.status === "running") {
|
|
19359
19570
|
throw new TaskEngineError(
|
|
19360
19571
|
`Task is already running: ${taskId}`,
|
|
@@ -19520,6 +19731,14 @@ var TaskEngine = class extends EventEmitter {
|
|
|
19520
19731
|
async executeTask(task, targetEngine, context, options) {
|
|
19521
19732
|
const taskId = task.id;
|
|
19522
19733
|
const abortController = new AbortController();
|
|
19734
|
+
let externalCancelled = false;
|
|
19735
|
+
const externalAbort = options.abortSignal ? () => {
|
|
19736
|
+
externalCancelled = true;
|
|
19737
|
+
abortController.abort();
|
|
19738
|
+
} : null;
|
|
19739
|
+
if (options.abortSignal && externalAbort) {
|
|
19740
|
+
options.abortSignal.addEventListener("abort", externalAbort, { once: true });
|
|
19741
|
+
}
|
|
19523
19742
|
const startTime = Date.now();
|
|
19524
19743
|
this.runningTasks.set(taskId, abortController);
|
|
19525
19744
|
this.store.updateTaskStatus(taskId, "running");
|
|
@@ -19560,7 +19779,7 @@ var TaskEngine = class extends EventEmitter {
|
|
|
19560
19779
|
const durationMs = Date.now() - startTime;
|
|
19561
19780
|
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
19562
19781
|
if (abortController.signal.aborted) {
|
|
19563
|
-
const taskError2 = { code: "TIMEOUT", message: `Task timed out after ${timeoutMs}ms` };
|
|
19782
|
+
const taskError2 = externalCancelled ? { code: "CANCELLED", message: "Task was cancelled by caller" } : { code: "TIMEOUT", message: `Task timed out after ${timeoutMs}ms` };
|
|
19564
19783
|
this.store.updateTaskFailed(taskId, taskError2, durationMs);
|
|
19565
19784
|
this.stats.failed++;
|
|
19566
19785
|
this.emit("task:failed", taskId, new TaskEngineError(taskError2.message, "EXECUTION_TIMEOUT"));
|
|
@@ -19594,6 +19813,9 @@ var TaskEngine = class extends EventEmitter {
|
|
|
19594
19813
|
} finally {
|
|
19595
19814
|
clearTimeout(timeoutId);
|
|
19596
19815
|
this.runningTasks.delete(taskId);
|
|
19816
|
+
if (options.abortSignal && externalAbort) {
|
|
19817
|
+
options.abortSignal.removeEventListener("abort", externalAbort);
|
|
19818
|
+
}
|
|
19597
19819
|
}
|
|
19598
19820
|
}
|
|
19599
19821
|
async executeWithRetry(task, engine, context, signal) {
|
|
@@ -19898,8 +20120,11 @@ var createTaskSchema = {
|
|
|
19898
20120
|
init_esm_shims();
|
|
19899
20121
|
init_logger();
|
|
19900
20122
|
function createRunTaskHandler(deps) {
|
|
19901
|
-
return async (input) => {
|
|
20123
|
+
return async (input, context) => {
|
|
19902
20124
|
const startTime = Date.now();
|
|
20125
|
+
if (context?.signal?.aborted) {
|
|
20126
|
+
throw new Error("Request was cancelled");
|
|
20127
|
+
}
|
|
19903
20128
|
logger.info("[run_task] Executing task", {
|
|
19904
20129
|
taskId: input.task_id,
|
|
19905
20130
|
engineOverride: input.engine_override,
|
|
@@ -19912,7 +20137,8 @@ function createRunTaskHandler(deps) {
|
|
|
19912
20137
|
const options = {
|
|
19913
20138
|
engineOverride: input.engine_override,
|
|
19914
20139
|
timeoutMs: input.timeout_ms,
|
|
19915
|
-
skipCache: input.skip_cache
|
|
20140
|
+
skipCache: input.skip_cache,
|
|
20141
|
+
abortSignal: context?.signal
|
|
19916
20142
|
};
|
|
19917
20143
|
const result = await taskEngine.runTask(input.task_id, options);
|
|
19918
20144
|
const output = {
|
|
@@ -22238,6 +22464,10 @@ var McpServer = class _McpServer {
|
|
|
22238
22464
|
version;
|
|
22239
22465
|
ajv;
|
|
22240
22466
|
compiledValidators = /* @__PURE__ */ new Map();
|
|
22467
|
+
cancelledRequests = /* @__PURE__ */ new Set();
|
|
22468
|
+
// Track client-initiated cancellations
|
|
22469
|
+
requestControllers = /* @__PURE__ */ new Map();
|
|
22470
|
+
// Abort long-running handlers
|
|
22241
22471
|
// v10.5.0: MCP Session for Smart Routing
|
|
22242
22472
|
session = null;
|
|
22243
22473
|
// v10.6.0: MCP Client Pool for cross-provider execution
|
|
@@ -22516,6 +22746,16 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22516
22746
|
if (this.streamingNotifier) {
|
|
22517
22747
|
this.streamingNotifier.stop();
|
|
22518
22748
|
}
|
|
22749
|
+
if (this.router) {
|
|
22750
|
+
this.router.destroy();
|
|
22751
|
+
}
|
|
22752
|
+
this.requestControllers.clear();
|
|
22753
|
+
if (this.sessionManager) {
|
|
22754
|
+
await this.sessionManager.destroy();
|
|
22755
|
+
}
|
|
22756
|
+
if (this.contextStore) {
|
|
22757
|
+
this.contextStore.destroy();
|
|
22758
|
+
}
|
|
22519
22759
|
if (this.eventBridge) {
|
|
22520
22760
|
this.eventBridge.destroy();
|
|
22521
22761
|
}
|
|
@@ -22633,6 +22873,12 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22633
22873
|
return this.handleToolsList(request, responseId);
|
|
22634
22874
|
case "tools/call":
|
|
22635
22875
|
return await this.handleToolCall(request, responseId);
|
|
22876
|
+
case "resources/list":
|
|
22877
|
+
return await this.handleResourcesList(request, responseId);
|
|
22878
|
+
case "resources/read":
|
|
22879
|
+
return await this.handleResourceRead(request, responseId);
|
|
22880
|
+
case "$/cancelRequest":
|
|
22881
|
+
return this.handleCancelRequest(request, responseId);
|
|
22636
22882
|
default:
|
|
22637
22883
|
return this.createErrorResponse(responseId, -32601 /* MethodNotFound */, `Method not found: ${method}`);
|
|
22638
22884
|
}
|
|
@@ -22688,6 +22934,48 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22688
22934
|
const tools = _McpServer.getStaticToolSchemas();
|
|
22689
22935
|
return { jsonrpc: "2.0", id, result: { tools } };
|
|
22690
22936
|
}
|
|
22937
|
+
/**
|
|
22938
|
+
* Handle resources/list request (exposes agent profiles as MCP resources)
|
|
22939
|
+
*/
|
|
22940
|
+
async handleResourcesList(_request, id) {
|
|
22941
|
+
await this.ensureInitialized();
|
|
22942
|
+
const agents = await this.profileLoader.listProfiles();
|
|
22943
|
+
const resources = agents.map((agent) => ({
|
|
22944
|
+
uri: `agent/${agent}`,
|
|
22945
|
+
name: `Agent: ${agent}`,
|
|
22946
|
+
description: `AutomatosX agent profile for ${agent}`,
|
|
22947
|
+
mimeType: "text/markdown"
|
|
22948
|
+
}));
|
|
22949
|
+
return { jsonrpc: "2.0", id, result: { resources } };
|
|
22950
|
+
}
|
|
22951
|
+
/**
|
|
22952
|
+
* Handle resources/read request
|
|
22953
|
+
*/
|
|
22954
|
+
async handleResourceRead(request, id) {
|
|
22955
|
+
await this.ensureInitialized();
|
|
22956
|
+
const uri = request.params?.uri;
|
|
22957
|
+
if (!uri || !uri.startsWith("agent/")) {
|
|
22958
|
+
return this.createErrorResponse(id, -32602 /* InvalidParams */, "Invalid resource URI. Expected agent/{name}.");
|
|
22959
|
+
}
|
|
22960
|
+
const agentName = uri.replace("agent/", "");
|
|
22961
|
+
try {
|
|
22962
|
+
const profile = await this.profileLoader.loadProfile(agentName);
|
|
22963
|
+
const summary = [
|
|
22964
|
+
`# ${agentName}`,
|
|
22965
|
+
profile.role ? `**Role:** ${profile.role}` : "",
|
|
22966
|
+
profile.abilities?.length ? `**Abilities:** ${profile.abilities.join(", ")}` : "",
|
|
22967
|
+
"",
|
|
22968
|
+
profile.systemPrompt || "No system prompt defined."
|
|
22969
|
+
].filter(Boolean).join("\n");
|
|
22970
|
+
const contents = [
|
|
22971
|
+
{ type: "text", text: summary },
|
|
22972
|
+
{ type: "application/json", json: profile }
|
|
22973
|
+
];
|
|
22974
|
+
return { jsonrpc: "2.0", id, result: { uri, mimeType: "text/markdown", contents } };
|
|
22975
|
+
} catch (error) {
|
|
22976
|
+
return this.createErrorResponse(id, -32603 /* InternalError */, `Failed to read resource: ${error.message}`);
|
|
22977
|
+
}
|
|
22978
|
+
}
|
|
22691
22979
|
/**
|
|
22692
22980
|
* Validate tool input against its JSON schema.
|
|
22693
22981
|
* Returns null if valid, or error message string if invalid.
|
|
@@ -22699,10 +22987,34 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22699
22987
|
return this.ajv.errorsText(validate.errors);
|
|
22700
22988
|
}
|
|
22701
22989
|
/** Create MCP tool response wrapper */
|
|
22702
|
-
createToolResponse(id,
|
|
22703
|
-
|
|
22990
|
+
createToolResponse(id, result, isError = false) {
|
|
22991
|
+
let content;
|
|
22992
|
+
if (typeof result === "string") {
|
|
22993
|
+
content = [{ type: "text", text: result }];
|
|
22994
|
+
} else {
|
|
22995
|
+
content = [
|
|
22996
|
+
{ type: "application/json", json: result },
|
|
22997
|
+
{ type: "text", text: JSON.stringify(result, null, 2) }
|
|
22998
|
+
];
|
|
22999
|
+
}
|
|
23000
|
+
const response = { content, ...isError && { isError } };
|
|
22704
23001
|
return { jsonrpc: "2.0", id, result: response };
|
|
22705
23002
|
}
|
|
23003
|
+
/**
|
|
23004
|
+
* Handle client cancellation ($/cancelRequest)
|
|
23005
|
+
*/
|
|
23006
|
+
handleCancelRequest(request, id) {
|
|
23007
|
+
const params = request.params;
|
|
23008
|
+
const cancelId = params?.id ?? params?.requestId;
|
|
23009
|
+
if (cancelId === void 0 || cancelId === null) {
|
|
23010
|
+
return this.createErrorResponse(id, -32602 /* InvalidParams */, "cancelRequest requires an id to cancel");
|
|
23011
|
+
}
|
|
23012
|
+
this.cancelledRequests.add(cancelId);
|
|
23013
|
+
logger.info("[MCP Server] Cancellation requested", { cancelId });
|
|
23014
|
+
const controller = this.requestControllers.get(cancelId);
|
|
23015
|
+
controller?.abort();
|
|
23016
|
+
return { jsonrpc: "2.0", id, result: null };
|
|
23017
|
+
}
|
|
22706
23018
|
/**
|
|
22707
23019
|
* Ensure services are initialized (lazy initialization on first call)
|
|
22708
23020
|
* OPTIMIZATION (v10.3.1): Moves 15-20s initialization from handshake to first request
|
|
@@ -22737,6 +23049,16 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22737
23049
|
async handleToolCall(request, id) {
|
|
22738
23050
|
const { name, arguments: args2 } = request.params;
|
|
22739
23051
|
logger.info("[MCP Server] Tool call", { tool: name });
|
|
23052
|
+
const requestId = id ?? null;
|
|
23053
|
+
const abortController = requestId !== null ? new AbortController() : null;
|
|
23054
|
+
if (requestId !== null && abortController) {
|
|
23055
|
+
this.requestControllers.set(requestId, abortController);
|
|
23056
|
+
}
|
|
23057
|
+
if (requestId !== null && this.cancelledRequests.has(requestId)) {
|
|
23058
|
+
this.cancelledRequests.delete(requestId);
|
|
23059
|
+
this.requestControllers.delete(requestId);
|
|
23060
|
+
return this.createErrorResponse(requestId, -32800 /* RequestCancelled */, "Request was cancelled");
|
|
23061
|
+
}
|
|
22740
23062
|
await this.ensureInitialized();
|
|
22741
23063
|
const handler = this.tools.get(name);
|
|
22742
23064
|
if (!handler) {
|
|
@@ -22747,11 +23069,27 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22747
23069
|
return this.createErrorResponse(id, -32602 /* InvalidParams */, validationError);
|
|
22748
23070
|
}
|
|
22749
23071
|
try {
|
|
22750
|
-
const result = await handler(args2 || {});
|
|
22751
|
-
|
|
23072
|
+
const result = await handler(args2 || {}, { signal: abortController?.signal });
|
|
23073
|
+
if (requestId !== null && this.cancelledRequests.has(requestId)) {
|
|
23074
|
+
this.cancelledRequests.delete(requestId);
|
|
23075
|
+
this.requestControllers.delete(requestId);
|
|
23076
|
+
return this.createErrorResponse(requestId, -32800 /* RequestCancelled */, "Request was cancelled");
|
|
23077
|
+
}
|
|
23078
|
+
return this.createToolResponse(id, result);
|
|
22752
23079
|
} catch (error) {
|
|
23080
|
+
const err = error;
|
|
23081
|
+
const cancelled = err?.name === "AbortError" || err?.message?.toLowerCase().includes("cancel");
|
|
23082
|
+
if (cancelled) {
|
|
23083
|
+
logger.info("[MCP Server] Tool execution cancelled", { tool: name, id: requestId ?? void 0 });
|
|
23084
|
+
return this.createErrorResponse(id, -32800 /* RequestCancelled */, "Request was cancelled");
|
|
23085
|
+
}
|
|
22753
23086
|
logger.error("[MCP Server] Tool execution failed", { tool: name, error });
|
|
22754
|
-
return this.createToolResponse(id, `Error: ${error
|
|
23087
|
+
return this.createToolResponse(id, `Error: ${err?.message ?? String(error)}`, true);
|
|
23088
|
+
} finally {
|
|
23089
|
+
if (requestId !== null) {
|
|
23090
|
+
this.cancelledRequests.delete(requestId);
|
|
23091
|
+
this.requestControllers.delete(requestId);
|
|
23092
|
+
}
|
|
22755
23093
|
}
|
|
22756
23094
|
}
|
|
22757
23095
|
/**
|
|
@@ -22761,26 +23099,34 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22761
23099
|
return { jsonrpc: "2.0", id, error: { code, message } };
|
|
22762
23100
|
}
|
|
22763
23101
|
/**
|
|
22764
|
-
* Write MCP-compliant response
|
|
23102
|
+
* Write MCP-compliant response using newline-delimited framing
|
|
23103
|
+
*
|
|
23104
|
+
* v12.2.0: Changed from Content-Length to newline-delimited framing
|
|
23105
|
+
* per official MCP specification: https://spec.modelcontextprotocol.io/specification/basic/transports/
|
|
23106
|
+
*
|
|
23107
|
+
* Format: JSON message followed by newline character
|
|
23108
|
+
* Reference: @modelcontextprotocol/sdk serializeMessage() uses: JSON.stringify(message) + '\n'
|
|
22765
23109
|
*/
|
|
22766
23110
|
writeResponse(response) {
|
|
22767
23111
|
const json = JSON.stringify(response);
|
|
22768
|
-
|
|
22769
|
-
|
|
22770
|
-
\r
|
|
22771
|
-
${json}`;
|
|
22772
|
-
process.stdout.write(message);
|
|
22773
|
-
logger.debug("[MCP Server] Response sent", { id: response.id, contentLength });
|
|
23112
|
+
process.stdout.write(json + "\n");
|
|
23113
|
+
logger.debug("[MCP Server] Response sent", { id: response.id, length: json.length });
|
|
22774
23114
|
}
|
|
22775
23115
|
/**
|
|
22776
|
-
* Start stdio server with
|
|
23116
|
+
* Start stdio server with newline-delimited framing
|
|
23117
|
+
*
|
|
23118
|
+
* v12.2.0: Changed from Content-Length to newline-delimited framing
|
|
23119
|
+
* per official MCP specification: https://spec.modelcontextprotocol.io/specification/basic/transports/
|
|
23120
|
+
*
|
|
23121
|
+
* Format: Each JSON-RPC message is terminated by a newline character.
|
|
23122
|
+
* Messages MUST NOT contain embedded newlines.
|
|
23123
|
+
* Reference: @modelcontextprotocol/sdk ReadBuffer uses: buffer.indexOf('\n')
|
|
22777
23124
|
*
|
|
22778
23125
|
* BUG FIX (v9.0.1): Added iteration limit and buffer size checks to prevent infinite loops
|
|
22779
23126
|
*/
|
|
22780
23127
|
async start() {
|
|
22781
23128
|
logger.info("[MCP Server] Starting stdio JSON-RPC server...");
|
|
22782
23129
|
let buffer = "";
|
|
22783
|
-
let contentLength = null;
|
|
22784
23130
|
process.stdin.on("data", (chunk) => {
|
|
22785
23131
|
void this.stdinMutex.runExclusive(async () => {
|
|
22786
23132
|
buffer += chunk.toString("utf-8");
|
|
@@ -22790,49 +23136,34 @@ ${json}`;
|
|
|
22790
23136
|
maxSize: STDIO_MAX_BUFFER_SIZE
|
|
22791
23137
|
});
|
|
22792
23138
|
buffer = "";
|
|
22793
|
-
contentLength = null;
|
|
22794
23139
|
return;
|
|
22795
23140
|
}
|
|
22796
23141
|
let iterations = 0;
|
|
22797
23142
|
while (iterations < STDIO_MAX_ITERATIONS) {
|
|
22798
23143
|
iterations++;
|
|
22799
|
-
|
|
22800
|
-
|
|
22801
|
-
|
|
22802
|
-
|
|
22803
|
-
|
|
22804
|
-
|
|
22805
|
-
|
|
22806
|
-
|
|
22807
|
-
|
|
22808
|
-
|
|
22809
|
-
|
|
22810
|
-
|
|
22811
|
-
|
|
22812
|
-
|
|
22813
|
-
|
|
22814
|
-
|
|
22815
|
-
|
|
22816
|
-
|
|
22817
|
-
|
|
22818
|
-
buffer = buffer.slice(headerEndIndex + delimiter2.length);
|
|
22819
|
-
contentLength = null;
|
|
22820
|
-
continue;
|
|
22821
|
-
}
|
|
23144
|
+
const newlineIndex = buffer.indexOf("\n");
|
|
23145
|
+
if (newlineIndex === -1) break;
|
|
23146
|
+
let jsonMessage = buffer.slice(0, newlineIndex);
|
|
23147
|
+
if (jsonMessage.endsWith("\r")) {
|
|
23148
|
+
jsonMessage = jsonMessage.slice(0, -1);
|
|
23149
|
+
}
|
|
23150
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
23151
|
+
if (jsonMessage.trim() === "") continue;
|
|
23152
|
+
if (jsonMessage.length > STDIO_MAX_MESSAGE_SIZE) {
|
|
23153
|
+
logger.error("[MCP Server] Message size exceeded maximum", {
|
|
23154
|
+
messageSize: jsonMessage.length,
|
|
23155
|
+
maxSize: STDIO_MAX_MESSAGE_SIZE
|
|
23156
|
+
});
|
|
23157
|
+
this.writeResponse({
|
|
23158
|
+
jsonrpc: "2.0",
|
|
23159
|
+
id: null,
|
|
23160
|
+
error: {
|
|
23161
|
+
code: -32600 /* InvalidRequest */,
|
|
23162
|
+
message: `Message too large: ${jsonMessage.length} bytes`
|
|
22822
23163
|
}
|
|
22823
|
-
}
|
|
22824
|
-
|
|
22825
|
-
logger.error("[MCP Server] No Content-Length header found");
|
|
22826
|
-
buffer = buffer.slice(headerEndIndex + delimiter2.length);
|
|
22827
|
-
continue;
|
|
22828
|
-
}
|
|
22829
|
-
buffer = buffer.slice(headerEndIndex + delimiter2.length);
|
|
23164
|
+
});
|
|
23165
|
+
continue;
|
|
22830
23166
|
}
|
|
22831
|
-
if (Buffer.byteLength(buffer, "utf-8") < contentLength) break;
|
|
22832
|
-
const messageBuffer = Buffer.from(buffer, "utf-8");
|
|
22833
|
-
const jsonMessage = messageBuffer.slice(0, contentLength).toString("utf-8");
|
|
22834
|
-
buffer = messageBuffer.slice(contentLength).toString("utf-8");
|
|
22835
|
-
contentLength = null;
|
|
22836
23167
|
try {
|
|
22837
23168
|
const request = JSON.parse(jsonMessage);
|
|
22838
23169
|
logger.debug("[MCP Server] Request received", { method: request.method, id: request.id });
|
|
@@ -22841,7 +23172,7 @@ ${json}`;
|
|
|
22841
23172
|
this.writeResponse(response);
|
|
22842
23173
|
}
|
|
22843
23174
|
} catch (error) {
|
|
22844
|
-
logger.error("[MCP Server] Failed to parse or handle request", {
|
|
23175
|
+
logger.error("[MCP Server] Failed to parse or handle request", { error: error.message });
|
|
22845
23176
|
this.writeResponse({ jsonrpc: "2.0", id: null, error: { code: -32700 /* ParseError */, message: "Parse error: Invalid JSON" } });
|
|
22846
23177
|
}
|
|
22847
23178
|
}
|
|
@@ -22860,7 +23191,7 @@ ${json}`;
|
|
|
22860
23191
|
process.stdin.on("end", () => shutdown("Server stopped (stdin closed)"));
|
|
22861
23192
|
process.on("SIGINT", () => shutdown("Received SIGINT, shutting down..."));
|
|
22862
23193
|
process.on("SIGTERM", () => shutdown("Received SIGTERM, shutting down..."));
|
|
22863
|
-
logger.info("[MCP Server] Server started successfully (
|
|
23194
|
+
logger.info("[MCP Server] Server started successfully (newline-delimited framing)");
|
|
22864
23195
|
}
|
|
22865
23196
|
};
|
|
22866
23197
|
|