@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/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
- if (entry.level === "error") {
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
- "featureFlags": {
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
- const result = await executeViaCli(agent, task, actualProvider, no_memory, deps, shouldTryMcp);
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 = this.generateTaskId();
18825
- const payloadHash = this.hashPayload(payloadJson);
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" ? this.estimateEngine(validated.type) : validated.engine;
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
- return new TaskStore(config);
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, text, isError = false) {
22703
- const response = { content: [{ type: "text", text }], ...isError && { isError } };
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
- return this.createToolResponse(id, JSON.stringify(result, null, 2));
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.message}`, true);
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 with Content-Length framing
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
- const contentLength = Buffer.byteLength(json, "utf-8");
22769
- const message = `Content-Length: ${contentLength}\r
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 Content-Length framing
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
- if (contentLength === null) {
22800
- const delimiter2 = buffer.includes("\r\n\r\n") ? "\r\n\r\n" : buffer.includes("\n\n") ? "\n\n" : null;
22801
- if (!delimiter2) break;
22802
- const headerEndIndex = buffer.indexOf(delimiter2);
22803
- const headerBlock = buffer.slice(0, headerEndIndex);
22804
- for (const line of headerBlock.split(delimiter2 === "\r\n\r\n" ? "\r\n" : "\n")) {
22805
- const [key, value] = line.split(":", 2).map((s) => s.trim());
22806
- if (key && key.toLowerCase() === "content-length" && value) {
22807
- contentLength = parseInt(value, 10);
22808
- if (isNaN(contentLength) || contentLength <= 0 || contentLength > STDIO_MAX_MESSAGE_SIZE) {
22809
- logger.error("[MCP Server] Invalid Content-Length", { contentLength });
22810
- this.writeResponse({
22811
- jsonrpc: "2.0",
22812
- id: null,
22813
- error: {
22814
- code: -32600 /* InvalidRequest */,
22815
- message: `Invalid Content-Length: ${contentLength}`
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
- if (contentLength === null) {
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", { jsonMessage, error });
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 (Content-Length framing)");
23194
+ logger.info("[MCP Server] Server started successfully (newline-delimited framing)");
22864
23195
  }
22865
23196
  };
22866
23197