@defai.digital/automatosx 11.3.4 → 11.4.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
@@ -3,7 +3,7 @@ import * as path4 from 'path';
3
3
  import path4__default, { dirname, join, extname as extname$1, basename, resolve, relative, isAbsolute, sep, parse, delimiter } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { mkdir, appendFile, readFile, readdir, writeFile, rename, unlink, copyFile, access, stat, realpath } from 'fs/promises';
6
- import * as fs3 from 'fs';
6
+ import * as fs4 from 'fs';
7
7
  import { existsSync, readFileSync, promises, mkdirSync, createWriteStream, writeFileSync, unlinkSync, constants } from 'fs';
8
8
  import Database2 from 'better-sqlite3';
9
9
  import { glob } from 'glob';
@@ -20,8 +20,9 @@ import { load } from 'js-yaml';
20
20
  import { EventEmitter } from 'events';
21
21
  import { findUp } from 'find-up';
22
22
  import * as sqliteVec from 'sqlite-vec';
23
- import { randomUUID } from 'crypto';
23
+ import { randomUUID, randomBytes, createHash } from 'crypto';
24
24
  import yaml from 'yaml';
25
+ import { gzipSync, gunzipSync } from 'zlib';
25
26
 
26
27
  var __defProp = Object.defineProperty;
27
28
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -318,16 +319,16 @@ var init_errors = __esm({
318
319
  constructor(message, code = "E1001" /* CONFIG_INVALID */, suggestions = [], context) {
319
320
  super(message, code, suggestions, context);
320
321
  }
321
- static notFound(path6) {
322
+ static notFound(path7) {
322
323
  return new _ConfigError(
323
- `Configuration file not found: ${path6}`,
324
+ `Configuration file not found: ${path7}`,
324
325
  "E1000" /* CONFIG_NOT_FOUND */,
325
326
  [
326
327
  'Run "automatosx setup" to create a new configuration',
327
328
  "Specify a custom config path with --config option",
328
329
  "Check that you are in a valid AutomatosX project directory"
329
330
  ],
330
- { path: path6 }
331
+ { path: path7 }
331
332
  );
332
333
  }
333
334
  static invalid(reason, context) {
@@ -342,7 +343,7 @@ var init_errors = __esm({
342
343
  context
343
344
  );
344
345
  }
345
- static parseError(error, path6) {
346
+ static parseError(error, path7) {
346
347
  return new _ConfigError(
347
348
  `Failed to parse configuration: ${error.message}`,
348
349
  "E1002" /* CONFIG_PARSE_ERROR */,
@@ -351,7 +352,7 @@ var init_errors = __esm({
351
352
  "Use a JSON validator to find syntax errors",
352
353
  'Reset to default with "automatosx setup --force"'
353
354
  ],
354
- { path: path6, originalError: error.message }
355
+ { path: path7, originalError: error.message }
355
356
  );
356
357
  }
357
358
  };
@@ -1164,11 +1165,11 @@ var init_workspace_indexer = __esm({
1164
1165
  /**
1165
1166
  * Detect file type and language
1166
1167
  */
1167
- detectTypeAndLanguage(path6, ext) {
1168
- if (path6.match(/package\.json|tsconfig\.json|\.config\.(js|ts|json|yaml|yml)|\.eslintrc|\.prettierrc/)) {
1168
+ detectTypeAndLanguage(path7, ext) {
1169
+ if (path7.match(/package\.json|tsconfig\.json|\.config\.(js|ts|json|yaml|yml)|\.eslintrc|\.prettierrc/)) {
1169
1170
  return { type: "config", language: "json" };
1170
1171
  }
1171
- if (path6.match(/\.(test|spec)\.(ts|js|tsx|jsx)$/) || path6.includes("__tests__")) {
1172
+ if (path7.match(/\.(test|spec)\.(ts|js|tsx|jsx)$/) || path7.includes("__tests__")) {
1172
1173
  const lang = LANGUAGE_MAP[ext];
1173
1174
  return { type: "test", language: lang };
1174
1175
  }
@@ -1185,15 +1186,15 @@ var init_workspace_indexer = __esm({
1185
1186
  *
1186
1187
  * Higher score = more likely to be relevant
1187
1188
  */
1188
- calculateImportance(path6, type, size) {
1189
+ calculateImportance(path7, type, size) {
1189
1190
  let score = 0.5;
1190
- if (path6.match(/^(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 1;
1191
- if (path6.match(/^src\/(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 0.95;
1192
- if (path6.match(/^src\/cli\/(index|main)\.(ts|js)$/)) score = 0.9;
1191
+ if (path7.match(/^(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 1;
1192
+ if (path7.match(/^src\/(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 0.95;
1193
+ if (path7.match(/^src\/cli\/(index|main)\.(ts|js)$/)) score = 0.9;
1193
1194
  if (type === "config") score = 0.85;
1194
- if (path6.includes("/core/")) score += 0.15;
1195
- if (path6.includes("/types/")) score += 0.1;
1196
- if (path6.includes("/utils/")) score += 0.05;
1195
+ if (path7.includes("/core/")) score += 0.15;
1196
+ if (path7.includes("/types/")) score += 0.1;
1197
+ if (path7.includes("/utils/")) score += 0.05;
1197
1198
  if (type === "test") score = Math.max(0.3, score - 0.3);
1198
1199
  if (type === "doc") score = Math.max(0.4, score - 0.2);
1199
1200
  if (size > 1e4) score += 0.05;
@@ -1669,10 +1670,10 @@ function findOnPathUnix(cmdBase) {
1669
1670
  try {
1670
1671
  const which = spawnSync("which", [cmdBase], { timeout: 3e3 });
1671
1672
  if (which.status === 0) {
1672
- const path6 = which.stdout.toString().trim();
1673
- if (path6) {
1674
- logger.debug("Found via which", { cmdBase, path: path6 });
1675
- return { found: true, path: path6 };
1673
+ const path7 = which.stdout.toString().trim();
1674
+ if (path7) {
1675
+ logger.debug("Found via which", { cmdBase, path: path7 });
1676
+ return { found: true, path: path7 };
1676
1677
  }
1677
1678
  }
1678
1679
  } catch (error) {
@@ -2800,7 +2801,7 @@ ${request.prompt}`;
2800
2801
  }
2801
2802
  if (process.env.AUTOMATOSX_DEBUG_PROMPT === "true") {
2802
2803
  const debugPath = path4.join(process.cwd(), "automatosx/tmp/debug-prompt.txt");
2803
- fs3.writeFileSync(debugPath, `=== FULL PROMPT SENT TO ${this.getCLICommand()} ===
2804
+ fs4.writeFileSync(debugPath, `=== FULL PROMPT SENT TO ${this.getCLICommand()} ===
2804
2805
 
2805
2806
  ${fullPrompt}
2806
2807
 
@@ -4711,11 +4712,11 @@ var VALIDATION_LIMITS = {
4711
4712
  MAX_PORT: 65535
4712
4713
  // Maximum port number
4713
4714
  };
4714
- function isValidRelativePath(path6) {
4715
- if (!path6 || typeof path6 !== "string") {
4715
+ function isValidRelativePath(path7) {
4716
+ if (!path7 || typeof path7 !== "string") {
4716
4717
  return false;
4717
4718
  }
4718
- const normalizedPath = path6.replace(/\\/g, "/");
4719
+ const normalizedPath = path7.replace(/\\/g, "/");
4719
4720
  if (normalizedPath.startsWith("/")) {
4720
4721
  return false;
4721
4722
  }
@@ -5432,7 +5433,7 @@ var safeNameSchema = z.string().min(1).max(VALIDATION_LIMITS.MAX_NAME_LENGTH).re
5432
5433
  "Name must be alphanumeric with dash/underscore only"
5433
5434
  ).describe("Safe identifier name");
5434
5435
  var relativePathSchema = z.string().min(1).refine(
5435
- (path6) => !path6.includes("..") && !path6.startsWith("/"),
5436
+ (path7) => !path7.includes("..") && !path7.startsWith("/"),
5436
5437
  "Path must be relative (no ../, no absolute paths)"
5437
5438
  ).describe("Relative path within project");
5438
5439
  var fileExtensionSchema = z.string().regex(
@@ -5772,16 +5773,16 @@ async function loadConfigUncached(projectDir) {
5772
5773
  });
5773
5774
  return config;
5774
5775
  }
5775
- async function loadConfigFile(path6) {
5776
+ async function loadConfigFile(path7) {
5776
5777
  try {
5777
- const content = await readFile(path6, "utf-8");
5778
+ const content = await readFile(path7, "utf-8");
5778
5779
  if (content.length > VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE) {
5779
5780
  throw ConfigError.parseError(
5780
5781
  new Error(`Config file too large (max ${VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE / 1024}KB, got ${Math.ceil(content.length / 1024)}KB)`),
5781
- path6
5782
+ path7
5782
5783
  );
5783
5784
  }
5784
- const ext = extname(path6).toLowerCase();
5785
+ const ext = extname(path7).toLowerCase();
5785
5786
  let userConfig;
5786
5787
  try {
5787
5788
  if (ext === ".yaml" || ext === ".yml") {
@@ -5790,7 +5791,7 @@ async function loadConfigFile(path6) {
5790
5791
  userConfig = JSON.parse(content);
5791
5792
  }
5792
5793
  } catch (parseError) {
5793
- throw ConfigError.parseError(parseError, path6);
5794
+ throw ConfigError.parseError(parseError, path7);
5794
5795
  }
5795
5796
  const config = mergeConfig(DEFAULT_CONFIG, userConfig);
5796
5797
  if (config.execution && userConfig.execution?.maxConcurrentAgents === void 0) {
@@ -5806,35 +5807,35 @@ async function loadConfigFile(path6) {
5806
5807
  if (validationErrors.length > 0) {
5807
5808
  throw ConfigError.invalid(
5808
5809
  validationErrors.join("; "),
5809
- { path: path6, errors: validationErrors }
5810
+ { path: path7, errors: validationErrors }
5810
5811
  );
5811
5812
  }
5812
- logger.info("Config loaded successfully", { path: normalizePath(path6), format: ext });
5813
+ logger.info("Config loaded successfully", { path: normalizePath(path7), format: ext });
5813
5814
  return config;
5814
5815
  } catch (error) {
5815
5816
  if (error instanceof ConfigError) {
5816
5817
  throw error;
5817
5818
  }
5818
5819
  if (error.code === "ENOENT") {
5819
- throw ConfigError.notFound(path6);
5820
+ throw ConfigError.notFound(path7);
5820
5821
  }
5821
5822
  if (error.code === "EACCES") {
5822
5823
  throw new ConfigError(
5823
- `Permission denied reading config: ${path6}`,
5824
+ `Permission denied reading config: ${path7}`,
5824
5825
  "E1002" /* CONFIG_PARSE_ERROR */,
5825
5826
  [
5826
5827
  "Check file permissions",
5827
5828
  "Run with appropriate user privileges",
5828
5829
  "Verify the file is accessible"
5829
5830
  ],
5830
- { path: path6, error: error.message }
5831
+ { path: path7, error: error.message }
5831
5832
  );
5832
5833
  }
5833
5834
  throw new ConfigError(
5834
5835
  `Failed to load config: ${error.message}`,
5835
5836
  "E1002" /* CONFIG_PARSE_ERROR */,
5836
5837
  ["Check file format and permissions"],
5837
- { path: path6, originalError: error.message }
5838
+ { path: path7, originalError: error.message }
5838
5839
  );
5839
5840
  }
5840
5841
  }
@@ -5850,8 +5851,8 @@ function validateConfigWithZod(config) {
5850
5851
  return ["Configuration validation failed with unknown error structure"];
5851
5852
  }
5852
5853
  return result.error.issues.map((err) => {
5853
- const path6 = err.path.join(".");
5854
- return `${path6}: ${err.message}`;
5854
+ const path7 = err.path.join(".");
5855
+ return `${path7}: ${err.message}`;
5855
5856
  });
5856
5857
  }
5857
5858
  function validateConfig(config) {
@@ -6261,8 +6262,8 @@ var PathError = class extends Error {
6261
6262
  };
6262
6263
 
6263
6264
  // src/shared/validation/path-resolver.ts
6264
- function isWindowsPath(path6) {
6265
- return /^[a-zA-Z]:[/\\]/.test(path6);
6265
+ function isWindowsPath(path7) {
6266
+ return /^[a-zA-Z]:[/\\]/.test(path7);
6266
6267
  }
6267
6268
  async function detectProjectRoot(startDir = process.cwd()) {
6268
6269
  if (process.env.AUTOMATOSX_PROJECT_ROOT) {
@@ -6362,8 +6363,8 @@ var PathResolver = class {
6362
6363
  /**
6363
6364
  * Validate path is within allowed base directory
6364
6365
  */
6365
- validatePath(path6, baseDir) {
6366
- const normalized = normalizePath(resolvePath(path6));
6366
+ validatePath(path7, baseDir) {
6367
+ const normalized = normalizePath(resolvePath(path7));
6367
6368
  const base = normalizePath(resolvePath(baseDir));
6368
6369
  const separator = "/";
6369
6370
  const pathWithSep = normalized + separator;
@@ -6373,15 +6374,15 @@ var PathResolver = class {
6373
6374
  /**
6374
6375
  * Check if path is within allowed boundaries
6375
6376
  */
6376
- isPathAllowed(path6) {
6377
- const boundary = this.checkBoundaries(path6);
6377
+ isPathAllowed(path7) {
6378
+ const boundary = this.checkBoundaries(path7);
6378
6379
  return boundary === "agent_workspace" || boundary === "user_project";
6379
6380
  }
6380
6381
  /**
6381
6382
  * Check which boundary a path belongs to
6382
6383
  */
6383
- checkBoundaries(path6) {
6384
- const normalized = resolvePath(path6);
6384
+ checkBoundaries(path7) {
6385
+ const normalized = resolvePath(path7);
6385
6386
  if (this.validatePath(normalized, this.config.agentWorkspace)) {
6386
6387
  return "agent_workspace";
6387
6388
  }
@@ -6399,15 +6400,15 @@ var PathResolver = class {
6399
6400
  /**
6400
6401
  * Get relative path from project root
6401
6402
  */
6402
- getRelativeToProject(path6) {
6403
- const normalized = resolvePath(path6);
6403
+ getRelativeToProject(path7) {
6404
+ const normalized = resolvePath(path7);
6404
6405
  return normalizePath(getRelativePath(this.config.projectDir, normalized));
6405
6406
  }
6406
6407
  /**
6407
6408
  * Get relative path from working directory
6408
6409
  */
6409
- getRelativeToWorking(path6) {
6410
- const normalized = resolvePath(path6);
6410
+ getRelativeToWorking(path7) {
6411
+ const normalized = resolvePath(path7);
6411
6412
  return normalizePath(getRelativePath(this.config.workingDir, normalized));
6412
6413
  }
6413
6414
  /**
@@ -6426,11 +6427,11 @@ var PathResolver = class {
6426
6427
  * Validate path is within project boundaries
6427
6428
  * @throws PathError if outside project
6428
6429
  */
6429
- validateInProject(path6) {
6430
- const boundary = this.checkBoundaries(path6);
6430
+ validateInProject(path7) {
6431
+ const boundary = this.checkBoundaries(path7);
6431
6432
  if (boundary === "outside_boundaries" || boundary === "system_restricted") {
6432
6433
  throw new PathError("Path outside project directory", {
6433
- path: path6,
6434
+ path: path7,
6434
6435
  projectDir: this.config.projectDir,
6435
6436
  boundary
6436
6437
  });
@@ -8297,18 +8298,18 @@ var ProviderSessionManager = class {
8297
8298
  };
8298
8299
  var providerSessionInstances = /* @__PURE__ */ new Map();
8299
8300
  async function getProviderSession(workspacePath) {
8300
- let path6;
8301
+ let path7;
8301
8302
  {
8302
8303
  if (process.env.NODE_ENV === "test" || process.env.VITEST) {
8303
- path6 = process.cwd();
8304
+ path7 = process.cwd();
8304
8305
  } else {
8305
- path6 = await detectProjectRoot();
8306
+ path7 = await detectProjectRoot();
8306
8307
  }
8307
8308
  }
8308
- if (!providerSessionInstances.has(path6)) {
8309
- providerSessionInstances.set(path6, new ProviderSessionManager(path6));
8309
+ if (!providerSessionInstances.has(path7)) {
8310
+ providerSessionInstances.set(path7, new ProviderSessionManager(path7));
8310
8311
  }
8311
- return providerSessionInstances.get(path6);
8312
+ return providerSessionInstances.get(path7);
8312
8313
  }
8313
8314
 
8314
8315
  // src/core/router/router.ts
@@ -15242,9 +15243,9 @@ var DependencyGraphBuilder = class {
15242
15243
  detectCycles(graph) {
15243
15244
  const visiting = /* @__PURE__ */ new Set();
15244
15245
  const visited = /* @__PURE__ */ new Set();
15245
- const visit = (nodeName, path6) => {
15246
+ const visit = (nodeName, path7) => {
15246
15247
  if (visiting.has(nodeName)) {
15247
- throw new Error(`Circular dependency detected: ${[...path6, nodeName].join(" \u2192 ")}`);
15248
+ throw new Error(`Circular dependency detected: ${[...path7, nodeName].join(" \u2192 ")}`);
15248
15249
  }
15249
15250
  if (visited.has(nodeName)) {
15250
15251
  return;
@@ -15259,7 +15260,7 @@ var DependencyGraphBuilder = class {
15259
15260
  }
15260
15261
  visiting.add(nodeName);
15261
15262
  for (const dependency of node.dependencies) {
15262
- visit(dependency, [...path6, nodeName]);
15263
+ visit(dependency, [...path7, nodeName]);
15263
15264
  }
15264
15265
  visiting.delete(nodeName);
15265
15266
  visited.add(nodeName);
@@ -16992,59 +16993,59 @@ var DANGEROUS_PATH_PATTERNS = [
16992
16993
  // Common data drive (Windows, alt format)
16993
16994
  ];
16994
16995
  var SUSPICIOUS_PATH_CHARS = /[<>:|"]/;
16995
- function validatePathParameter(path6, paramName, projectRoot = process.cwd()) {
16996
- if (!path6 || path6.trim() === "") {
16996
+ function validatePathParameter(path7, paramName, projectRoot = process.cwd()) {
16997
+ if (!path7 || path7.trim() === "") {
16997
16998
  throw new ValidationError(
16998
16999
  `Invalid ${paramName}: path cannot be empty`,
16999
17000
  -32602 /* InvalidParams */,
17000
- { path: path6, paramName }
17001
+ { path: path7, paramName }
17001
17002
  );
17002
17003
  }
17003
17004
  for (const pattern of DANGEROUS_PATH_PATTERNS) {
17004
- if (path6.includes(pattern)) {
17005
+ if (path7.includes(pattern)) {
17005
17006
  throw new ValidationError(
17006
17007
  `Invalid ${paramName}: path contains dangerous pattern "${pattern}"`,
17007
17008
  -32602 /* InvalidParams */,
17008
- { path: path6, paramName, pattern }
17009
+ { path: path7, paramName, pattern }
17009
17010
  );
17010
17011
  }
17011
17012
  }
17012
- if (isAbsolute(path6)) {
17013
+ if (isAbsolute(path7)) {
17013
17014
  throw new ValidationError(
17014
17015
  `Invalid ${paramName}: absolute paths are not allowed`,
17015
17016
  -32602 /* InvalidParams */,
17016
- { path: path6, paramName }
17017
+ { path: path7, paramName }
17017
17018
  );
17018
17019
  }
17019
17020
  try {
17020
- const resolvedPath = resolve(projectRoot, path6);
17021
+ const resolvedPath = resolve(projectRoot, path7);
17021
17022
  const normalizedRoot = resolve(projectRoot);
17022
17023
  if (!resolvedPath.startsWith(normalizedRoot + sep) && resolvedPath !== normalizedRoot) {
17023
17024
  throw new ValidationError(
17024
17025
  `Invalid ${paramName}: path escapes project boundary`,
17025
17026
  -32602 /* InvalidParams */,
17026
- { path: path6, paramName, projectRoot, resolvedPath }
17027
+ { path: path7, paramName, projectRoot, resolvedPath }
17027
17028
  );
17028
17029
  }
17029
17030
  } catch (error) {
17030
17031
  throw new ValidationError(
17031
17032
  `Invalid ${paramName}: path resolution failed`,
17032
17033
  -32602 /* InvalidParams */,
17033
- { path: path6, paramName, error: String(error) }
17034
+ { path: path7, paramName, error: String(error) }
17034
17035
  );
17035
17036
  }
17036
- if (path6.includes("\0")) {
17037
+ if (path7.includes("\0")) {
17037
17038
  throw new ValidationError(
17038
17039
  `Invalid ${paramName}: path contains null byte`,
17039
17040
  -32602 /* InvalidParams */,
17040
- { path: path6, paramName }
17041
+ { path: path7, paramName }
17041
17042
  );
17042
17043
  }
17043
- if (SUSPICIOUS_PATH_CHARS.test(path6)) {
17044
+ if (SUSPICIOUS_PATH_CHARS.test(path7)) {
17044
17045
  throw new ValidationError(
17045
17046
  `Invalid ${paramName}: path contains invalid characters`,
17046
17047
  -32602 /* InvalidParams */,
17047
- { path: path6, paramName }
17048
+ { path: path7, paramName }
17048
17049
  );
17049
17050
  }
17050
17051
  }
@@ -17740,8 +17741,8 @@ function createMemoryExportHandler(deps) {
17740
17741
  return async (input) => {
17741
17742
  logger.info("[MCP] memory_export called", { input });
17742
17743
  try {
17743
- const { path: path6 } = input;
17744
- const absolutePath = resolveExportPath(deps.pathResolver, path6);
17744
+ const { path: path7 } = input;
17745
+ const absolutePath = resolveExportPath(deps.pathResolver, path7);
17745
17746
  const exported = await deps.memoryManager.exportToJSON(absolutePath);
17746
17747
  const result = {
17747
17748
  success: true,
@@ -17777,8 +17778,8 @@ function createMemoryImportHandler(deps) {
17777
17778
  return async (input) => {
17778
17779
  logger.info("[MCP] memory_import called", { input });
17779
17780
  try {
17780
- const { path: path6 } = input;
17781
- const absolutePath = resolveImportPath(deps.pathResolver, path6);
17781
+ const { path: path7 } = input;
17782
+ const absolutePath = resolveImportPath(deps.pathResolver, path7);
17782
17783
  const imported = await deps.memoryManager.importFromJSON(absolutePath);
17783
17784
  const result = {
17784
17785
  success: true,
@@ -18133,6 +18134,1930 @@ Task: ${task}`;
18133
18134
  };
18134
18135
  }
18135
18136
 
18137
+ // src/mcp/tools/task/index.ts
18138
+ init_esm_shims();
18139
+
18140
+ // src/mcp/tools/task/create-task.ts
18141
+ init_esm_shims();
18142
+
18143
+ // src/core/task-engine/index.ts
18144
+ init_esm_shims();
18145
+
18146
+ // src/core/task-engine/types.ts
18147
+ init_esm_shims();
18148
+ var TaskEngineError = class _TaskEngineError extends Error {
18149
+ constructor(message, code, details) {
18150
+ super(message);
18151
+ this.code = code;
18152
+ this.details = details;
18153
+ this.name = "TaskEngineError";
18154
+ Error.captureStackTrace?.(this, _TaskEngineError);
18155
+ }
18156
+ };
18157
+ var LoopPreventionError = class extends TaskEngineError {
18158
+ constructor(message, callChain, code = "LOOP_DETECTED") {
18159
+ super(message, code, { callChain });
18160
+ this.callChain = callChain;
18161
+ this.name = "LoopPreventionError";
18162
+ }
18163
+ };
18164
+ var TaskTypeSchema = z.enum([
18165
+ "web_search",
18166
+ "code_review",
18167
+ "code_generation",
18168
+ "analysis",
18169
+ "custom"
18170
+ ]);
18171
+ var TaskEngineSchema = z.enum([
18172
+ "auto",
18173
+ "gemini",
18174
+ "claude",
18175
+ "codex",
18176
+ "ax-cli"
18177
+ ]);
18178
+ var TaskStatusSchema = z.enum([
18179
+ "pending",
18180
+ "running",
18181
+ "completed",
18182
+ "failed",
18183
+ "expired"
18184
+ ]);
18185
+ var OriginClientSchema = z.enum([
18186
+ "claude-code",
18187
+ "gemini-cli",
18188
+ "codex-cli",
18189
+ "ax-cli",
18190
+ "unknown"
18191
+ ]);
18192
+ var CreateTaskInputSchema = z.object({
18193
+ type: TaskTypeSchema,
18194
+ payload: z.record(z.string(), z.unknown()),
18195
+ engine: TaskEngineSchema.default("auto"),
18196
+ priority: z.number().int().min(1).max(10).default(5),
18197
+ ttlHours: z.number().int().min(1).default(24).transform((v) => Math.min(v, 168)),
18198
+ context: z.object({
18199
+ originClient: OriginClientSchema.optional(),
18200
+ callChain: z.array(z.string()).optional(),
18201
+ depth: z.number().int().min(0).optional()
18202
+ }).optional()
18203
+ });
18204
+ var TaskFilterSchema = z.object({
18205
+ status: TaskStatusSchema.optional(),
18206
+ engine: TaskEngineSchema.optional(),
18207
+ type: TaskTypeSchema.optional(),
18208
+ originClient: OriginClientSchema.optional(),
18209
+ limit: z.number().int().min(1).max(1e3).default(20),
18210
+ offset: z.number().int().min(0).default(0)
18211
+ });
18212
+ z.object({
18213
+ engineOverride: z.enum(["gemini", "claude", "codex", "ax-cli"]).optional(),
18214
+ timeoutMs: z.number().int().min(5e3).max(3e5).optional(),
18215
+ skipCache: z.boolean().default(false)
18216
+ });
18217
+ function isLoopPreventionError(error) {
18218
+ return error instanceof LoopPreventionError;
18219
+ }
18220
+
18221
+ // src/core/task-engine/loop-guard.ts
18222
+ init_esm_shims();
18223
+ var DEFAULT_BLOCKED_PATTERNS = [
18224
+ // Prevent recursive hub pattern
18225
+ /automatosx.*automatosx/
18226
+ ];
18227
+ var CLIENT_NORMALIZATION_MAP = {
18228
+ // Claude variants
18229
+ "claude": "claude-code",
18230
+ "claude-code": "claude-code",
18231
+ "claudecode": "claude-code",
18232
+ "anthropic": "claude-code",
18233
+ // Gemini variants
18234
+ "gemini": "gemini-cli",
18235
+ "gemini-cli": "gemini-cli",
18236
+ "geminicli": "gemini-cli",
18237
+ "google": "gemini-cli",
18238
+ // Codex variants
18239
+ "codex": "codex-cli",
18240
+ "codex-cli": "codex-cli",
18241
+ "openai": "codex-cli",
18242
+ "gpt": "codex-cli",
18243
+ // ax-cli variants
18244
+ "ax": "ax-cli",
18245
+ "ax-cli": "ax-cli",
18246
+ "axcli": "ax-cli"
18247
+ };
18248
+ var LoopGuard = class {
18249
+ config;
18250
+ constructor(config = {}) {
18251
+ this.config = {
18252
+ maxDepth: config.maxDepth ?? 2,
18253
+ maxChainLength: config.maxChainLength ?? 5,
18254
+ blockSelfCalls: config.blockSelfCalls ?? true,
18255
+ blockedPatterns: config.blockedPatterns ?? DEFAULT_BLOCKED_PATTERNS
18256
+ };
18257
+ if (this.config.maxDepth < 1 || this.config.maxDepth > 10) {
18258
+ throw new Error("LoopGuard: maxDepth must be between 1 and 10");
18259
+ }
18260
+ if (this.config.maxChainLength < 2 || this.config.maxChainLength > 20) {
18261
+ throw new Error("LoopGuard: maxChainLength must be between 2 and 20");
18262
+ }
18263
+ }
18264
+ /**
18265
+ * Validate that a task execution is allowed
18266
+ *
18267
+ * @param ctx - Current task context
18268
+ * @param targetEngine - Engine to route the task to
18269
+ * @throws {LoopPreventionError} If execution would cause a loop
18270
+ */
18271
+ validateExecution(ctx, targetEngine) {
18272
+ const normalizedTarget = this.normalizeClient(targetEngine);
18273
+ const projectedChain = [...ctx.callChain, "automatosx", normalizedTarget];
18274
+ if (ctx.depth >= this.config.maxDepth) {
18275
+ throw new LoopPreventionError(
18276
+ `Maximum depth exceeded: current depth ${ctx.depth} >= limit ${this.config.maxDepth}`,
18277
+ projectedChain,
18278
+ "DEPTH_EXCEEDED"
18279
+ );
18280
+ }
18281
+ if (this.config.blockSelfCalls) {
18282
+ const engineInChain = ctx.callChain.some(
18283
+ (entry) => this.normalizeClient(entry) === normalizedTarget
18284
+ );
18285
+ if (engineInChain) {
18286
+ throw new LoopPreventionError(
18287
+ `Circular call detected: ${normalizedTarget} already in chain [${ctx.callChain.join(" \u2192 ")}]`,
18288
+ projectedChain,
18289
+ "LOOP_DETECTED"
18290
+ );
18291
+ }
18292
+ }
18293
+ if (projectedChain.length > this.config.maxChainLength) {
18294
+ throw new LoopPreventionError(
18295
+ `Call chain too long: ${projectedChain.length} > ${this.config.maxChainLength}`,
18296
+ projectedChain,
18297
+ "CHAIN_TOO_LONG"
18298
+ );
18299
+ }
18300
+ const chainString = projectedChain.join("\u2192");
18301
+ for (const pattern of this.config.blockedPatterns) {
18302
+ if (pattern.test(chainString)) {
18303
+ throw new LoopPreventionError(
18304
+ `Blocked pattern matched: ${chainString} matches ${pattern.toString()}`,
18305
+ projectedChain,
18306
+ "BLOCKED_PATTERN"
18307
+ );
18308
+ }
18309
+ }
18310
+ }
18311
+ /**
18312
+ * Create a new task context for an incoming request
18313
+ *
18314
+ * @param originClient - The client that initiated the request
18315
+ * @param taskId - Optional task ID (will be set later if not provided)
18316
+ * @returns New task context
18317
+ */
18318
+ createContext(originClient, taskId = "") {
18319
+ const normalized = this.normalizeClient(originClient);
18320
+ return {
18321
+ taskId,
18322
+ originClient: normalized,
18323
+ callChain: [normalized],
18324
+ depth: 0,
18325
+ maxDepth: this.config.maxDepth,
18326
+ createdAt: Date.now()
18327
+ };
18328
+ }
18329
+ /**
18330
+ * Extend a task context for a nested call
18331
+ *
18332
+ * @param ctx - Current task context
18333
+ * @param engine - Engine being called
18334
+ * @returns Extended task context
18335
+ */
18336
+ extendContext(ctx, engine) {
18337
+ const normalizedEngine = this.normalizeClient(engine);
18338
+ return {
18339
+ ...ctx,
18340
+ callChain: [...ctx.callChain, "automatosx", normalizedEngine],
18341
+ depth: ctx.depth + 1,
18342
+ createdAt: Date.now()
18343
+ };
18344
+ }
18345
+ /**
18346
+ * Merge an incoming context with the current context
18347
+ * Used when a task already has context from a previous hop
18348
+ *
18349
+ * @param incomingCtx - Context from incoming request
18350
+ * @param currentOrigin - Current origin (automatosx)
18351
+ * @returns Merged context
18352
+ */
18353
+ mergeContext(incomingCtx, currentOrigin = "automatosx") {
18354
+ const callChain = incomingCtx.callChain ?? [];
18355
+ const originClient = incomingCtx.originClient ?? "unknown";
18356
+ const mergedChain = callChain.length > 0 ? [...callChain, currentOrigin] : [originClient, currentOrigin];
18357
+ return {
18358
+ taskId: incomingCtx.taskId ?? "",
18359
+ originClient: this.normalizeClient(originClient),
18360
+ callChain: mergedChain,
18361
+ depth: (incomingCtx.depth ?? 0) + 1,
18362
+ maxDepth: this.config.maxDepth,
18363
+ createdAt: Date.now()
18364
+ };
18365
+ }
18366
+ /**
18367
+ * Get a human-readable representation of the call chain
18368
+ *
18369
+ * @param ctx - Task context
18370
+ * @returns Formatted call chain string
18371
+ */
18372
+ getCallChainString(ctx) {
18373
+ return ctx.callChain.join(" \u2192 ");
18374
+ }
18375
+ /**
18376
+ * Check if a context is valid (not expired or corrupted)
18377
+ *
18378
+ * @param ctx - Task context to validate
18379
+ * @returns true if context is valid
18380
+ */
18381
+ isValidContext(ctx) {
18382
+ if (!ctx || typeof ctx !== "object") {
18383
+ return false;
18384
+ }
18385
+ const context = ctx;
18386
+ return typeof context.taskId === "string" && typeof context.originClient === "string" && Array.isArray(context.callChain) && context.callChain.every((item) => typeof item === "string") && typeof context.depth === "number" && Number.isInteger(context.depth) && context.depth >= 0 && typeof context.maxDepth === "number" && typeof context.createdAt === "number";
18387
+ }
18388
+ /**
18389
+ * Get current configuration
18390
+ */
18391
+ getConfig() {
18392
+ return { ...this.config };
18393
+ }
18394
+ /**
18395
+ * Normalize client name to standard format
18396
+ *
18397
+ * IMPORTANT: Unknown engines are preserved as-is (normalized formatting only)
18398
+ * to prevent false-positive loop detection when two different unknown engines
18399
+ * would both map to 'unknown' and trigger a circular call error.
18400
+ */
18401
+ normalizeClient(client) {
18402
+ if (!client || typeof client !== "string") {
18403
+ return "unknown";
18404
+ }
18405
+ const normalized = client.toLowerCase().trim().replace(/[\s-_]+/g, "-");
18406
+ if (CLIENT_NORMALIZATION_MAP[normalized]) {
18407
+ return CLIENT_NORMALIZATION_MAP[normalized];
18408
+ }
18409
+ return normalized;
18410
+ }
18411
+ };
18412
+ function createLoopGuard(config) {
18413
+ return new LoopGuard(config);
18414
+ }
18415
+
18416
+ // src/core/task-engine/compression.ts
18417
+ init_esm_shims();
18418
+ var DEFAULT_COMPRESSION_LEVEL = 6;
18419
+ var MIN_COMPRESSION_THRESHOLD = 4096;
18420
+ function decompressPayload(compressed) {
18421
+ try {
18422
+ const decompressed = gunzipSync(compressed);
18423
+ const json = decompressed.toString("utf-8");
18424
+ return JSON.parse(json);
18425
+ } catch (error) {
18426
+ throw new TaskEngineError(
18427
+ `Failed to decompress payload: ${error instanceof Error ? error.message : "Unknown error"}`,
18428
+ "COMPRESSION_ERROR",
18429
+ { originalError: error }
18430
+ );
18431
+ }
18432
+ }
18433
+ function compressWithInfo(payload, options = {}) {
18434
+ const level = options.level ?? DEFAULT_COMPRESSION_LEVEL;
18435
+ const minThreshold = options.minThreshold ?? MIN_COMPRESSION_THRESHOLD;
18436
+ try {
18437
+ const json = JSON.stringify(payload);
18438
+ const originalBuffer = Buffer.from(json, "utf-8");
18439
+ const originalSize = originalBuffer.length;
18440
+ if (originalSize < minThreshold) {
18441
+ return {
18442
+ data: originalBuffer,
18443
+ originalSize,
18444
+ compressedSize: originalSize,
18445
+ ratio: 1,
18446
+ compressed: false
18447
+ };
18448
+ }
18449
+ const compressedBuffer = gzipSync(originalBuffer, {
18450
+ level: Math.min(Math.max(level, 1), 9)
18451
+ });
18452
+ const compressedSize = compressedBuffer.length;
18453
+ if (compressedSize >= originalSize) {
18454
+ return {
18455
+ data: originalBuffer,
18456
+ originalSize,
18457
+ compressedSize: originalSize,
18458
+ ratio: 1,
18459
+ compressed: false
18460
+ };
18461
+ }
18462
+ return {
18463
+ data: compressedBuffer,
18464
+ originalSize,
18465
+ compressedSize,
18466
+ ratio: originalSize / compressedSize,
18467
+ compressed: true
18468
+ };
18469
+ } catch (error) {
18470
+ throw new TaskEngineError(
18471
+ `Failed to compress payload: ${error instanceof Error ? error.message : "Unknown error"}`,
18472
+ "COMPRESSION_ERROR",
18473
+ { originalError: error }
18474
+ );
18475
+ }
18476
+ }
18477
+
18478
+ // src/core/task-engine/store.ts
18479
+ init_esm_shims();
18480
+ init_factory();
18481
+ init_logger();
18482
+ var DEFAULT_CONFIG3 = {
18483
+ dbPath: ".automatosx/tasks/tasks.db",
18484
+ maxPayloadBytes: 1024 * 1024,
18485
+ // 1MB
18486
+ compressionEnabled: true,
18487
+ compressionLevel: 6,
18488
+ defaultTtlHours: 24,
18489
+ maxTtlHours: 168,
18490
+ // 7 days
18491
+ busyTimeout: 5e3
18492
+ };
18493
+ var SQL = {
18494
+ CREATE_TABLE: `
18495
+ CREATE TABLE IF NOT EXISTS task_store (
18496
+ id TEXT PRIMARY KEY,
18497
+ type TEXT NOT NULL CHECK (type IN ('web_search', 'code_review', 'code_generation', 'analysis', 'custom')),
18498
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'completed', 'failed', 'expired')),
18499
+ engine TEXT CHECK (engine IN ('gemini', 'claude', 'codex', 'ax-cli', NULL)),
18500
+ priority INTEGER NOT NULL DEFAULT 5 CHECK (priority BETWEEN 1 AND 10),
18501
+
18502
+ payload_compressed BLOB NOT NULL,
18503
+ payload_size_bytes INTEGER NOT NULL,
18504
+ payload_hash TEXT NOT NULL,
18505
+ is_compressed INTEGER NOT NULL DEFAULT 1,
18506
+
18507
+ result_compressed BLOB,
18508
+ result_size_bytes INTEGER,
18509
+ result_is_compressed INTEGER,
18510
+
18511
+ origin_client TEXT NOT NULL,
18512
+ call_chain TEXT NOT NULL,
18513
+ depth INTEGER NOT NULL DEFAULT 0,
18514
+
18515
+ created_at INTEGER NOT NULL,
18516
+ started_at INTEGER,
18517
+ completed_at INTEGER,
18518
+ expires_at INTEGER NOT NULL,
18519
+
18520
+ duration_ms INTEGER,
18521
+ tokens_prompt INTEGER,
18522
+ tokens_completion INTEGER,
18523
+ retry_count INTEGER NOT NULL DEFAULT 0,
18524
+
18525
+ error_code TEXT,
18526
+ error_message TEXT
18527
+ )
18528
+ `,
18529
+ CREATE_INDEXES: `
18530
+ CREATE INDEX IF NOT EXISTS idx_task_status ON task_store(status);
18531
+ CREATE INDEX IF NOT EXISTS idx_task_status_priority ON task_store(status, priority DESC);
18532
+ CREATE INDEX IF NOT EXISTS idx_task_expires ON task_store(expires_at);
18533
+ CREATE INDEX IF NOT EXISTS idx_task_engine ON task_store(engine);
18534
+ CREATE INDEX IF NOT EXISTS idx_task_created ON task_store(created_at DESC);
18535
+ CREATE INDEX IF NOT EXISTS idx_task_payload_hash ON task_store(payload_hash);
18536
+ CREATE INDEX IF NOT EXISTS idx_task_origin ON task_store(origin_client);
18537
+ `,
18538
+ INSERT_TASK: `
18539
+ INSERT INTO task_store (
18540
+ id, type, status, engine, priority,
18541
+ payload_compressed, payload_size_bytes, payload_hash, is_compressed,
18542
+ origin_client, call_chain, depth,
18543
+ created_at, expires_at
18544
+ ) VALUES (
18545
+ :id, :type, :status, :engine, :priority,
18546
+ :payload_compressed, :payload_size_bytes, :payload_hash, :is_compressed,
18547
+ :origin_client, :call_chain, :depth,
18548
+ :created_at, :expires_at
18549
+ )
18550
+ `,
18551
+ GET_BY_ID: `
18552
+ SELECT * FROM task_store WHERE id = ?
18553
+ `,
18554
+ UPDATE_STATUS: `
18555
+ UPDATE task_store SET
18556
+ status = :status,
18557
+ started_at = COALESCE(:started_at, started_at),
18558
+ completed_at = COALESCE(:completed_at, completed_at),
18559
+ error_code = COALESCE(:error_code, error_code),
18560
+ error_message = COALESCE(:error_message, error_message)
18561
+ WHERE id = :id
18562
+ `,
18563
+ UPDATE_RESULT: `
18564
+ UPDATE task_store SET
18565
+ status = 'completed',
18566
+ result_compressed = :result_compressed,
18567
+ result_size_bytes = :result_size_bytes,
18568
+ result_is_compressed = :result_is_compressed,
18569
+ completed_at = :completed_at,
18570
+ duration_ms = :duration_ms,
18571
+ tokens_prompt = :tokens_prompt,
18572
+ tokens_completion = :tokens_completion
18573
+ WHERE id = :id
18574
+ `,
18575
+ UPDATE_FAILED: `
18576
+ UPDATE task_store SET
18577
+ status = 'failed',
18578
+ completed_at = :completed_at,
18579
+ duration_ms = :duration_ms,
18580
+ error_code = :error_code,
18581
+ error_message = :error_message,
18582
+ retry_count = retry_count + 1
18583
+ WHERE id = :id
18584
+ `,
18585
+ INCREMENT_RETRY: `
18586
+ UPDATE task_store SET retry_count = retry_count + 1 WHERE id = ?
18587
+ `,
18588
+ DELETE_TASK: `
18589
+ DELETE FROM task_store WHERE id = ?
18590
+ `,
18591
+ CLEANUP_EXPIRED: `
18592
+ DELETE FROM task_store WHERE expires_at < ? AND status NOT IN ('running')
18593
+ `,
18594
+ CLEANUP_ZOMBIE_RUNNING: `
18595
+ UPDATE task_store SET
18596
+ status = 'failed',
18597
+ completed_at = :completed_at,
18598
+ error_code = 'ZOMBIE_TASK',
18599
+ error_message = 'Task was stuck in running state past expiry'
18600
+ WHERE status = 'running' AND expires_at < :now
18601
+ `,
18602
+ COUNT_BY_STATUS: `
18603
+ SELECT status, COUNT(*) as count FROM task_store GROUP BY status
18604
+ `,
18605
+ FIND_BY_HASH: `
18606
+ SELECT id FROM task_store WHERE payload_hash = ? AND status = 'completed' LIMIT 1
18607
+ `
18608
+ };
18609
+ var TaskStore = class {
18610
+ db;
18611
+ config;
18612
+ closed = false;
18613
+ // Prepared statements
18614
+ stmtInsert;
18615
+ stmtGetById;
18616
+ stmtUpdateStatus;
18617
+ stmtUpdateResult;
18618
+ stmtUpdateFailed;
18619
+ stmtIncrementRetry;
18620
+ stmtDelete;
18621
+ stmtCleanup;
18622
+ stmtCleanupZombies;
18623
+ stmtCountByStatus;
18624
+ stmtFindByHash;
18625
+ constructor(config = {}) {
18626
+ this.config = { ...DEFAULT_CONFIG3, ...config };
18627
+ this.db = DatabaseFactory.create(this.config.dbPath, {
18628
+ busyTimeout: this.config.busyTimeout,
18629
+ enableWal: true
18630
+ });
18631
+ this.initializeSchema();
18632
+ this.prepareStatements();
18633
+ logger.debug("TaskStore initialized", {
18634
+ dbPath: this.config.dbPath,
18635
+ maxPayloadBytes: this.config.maxPayloadBytes,
18636
+ compressionEnabled: this.config.compressionEnabled
18637
+ });
18638
+ }
18639
+ /**
18640
+ * Create a new task
18641
+ */
18642
+ createTask(input) {
18643
+ this.ensureOpen();
18644
+ const validated = CreateTaskInputSchema.parse(input);
18645
+ const payloadJson = JSON.stringify(validated.payload);
18646
+ const payloadSize = Buffer.byteLength(payloadJson, "utf-8");
18647
+ if (payloadSize > this.config.maxPayloadBytes) {
18648
+ throw new TaskEngineError(
18649
+ `Payload size ${payloadSize} exceeds limit ${this.config.maxPayloadBytes}`,
18650
+ "PAYLOAD_TOO_LARGE",
18651
+ { payloadSize, limit: this.config.maxPayloadBytes }
18652
+ );
18653
+ }
18654
+ let payloadBuffer;
18655
+ let isCompressed;
18656
+ let compressionRatio;
18657
+ if (this.config.compressionEnabled) {
18658
+ const result = compressWithInfo(validated.payload, {
18659
+ level: this.config.compressionLevel
18660
+ });
18661
+ payloadBuffer = result.data;
18662
+ isCompressed = result.compressed;
18663
+ compressionRatio = result.ratio;
18664
+ } else {
18665
+ payloadBuffer = Buffer.from(payloadJson, "utf-8");
18666
+ isCompressed = false;
18667
+ compressionRatio = 1;
18668
+ }
18669
+ const taskId = this.generateTaskId();
18670
+ const payloadHash = this.hashPayload(payloadJson);
18671
+ const now = Date.now();
18672
+ const ttlHours = Math.min(
18673
+ validated.ttlHours ?? this.config.defaultTtlHours,
18674
+ this.config.maxTtlHours
18675
+ );
18676
+ const expiresAt = now + ttlHours * 60 * 60 * 1e3;
18677
+ const estimatedEngine = validated.engine === "auto" ? this.estimateEngine(validated.type) : validated.engine;
18678
+ try {
18679
+ this.stmtInsert.run({
18680
+ id: taskId,
18681
+ type: validated.type,
18682
+ status: "pending",
18683
+ engine: validated.engine === "auto" ? null : validated.engine,
18684
+ priority: validated.priority ?? 5,
18685
+ payload_compressed: payloadBuffer,
18686
+ payload_size_bytes: payloadSize,
18687
+ payload_hash: payloadHash,
18688
+ is_compressed: isCompressed ? 1 : 0,
18689
+ origin_client: validated.context?.originClient ?? "unknown",
18690
+ call_chain: JSON.stringify(validated.context?.callChain ?? []),
18691
+ depth: validated.context?.depth ?? 0,
18692
+ created_at: now,
18693
+ expires_at: expiresAt
18694
+ });
18695
+ } catch (error) {
18696
+ throw new TaskEngineError(
18697
+ `Failed to create task: ${error instanceof Error ? error.message : "Unknown error"}`,
18698
+ "STORE_ERROR",
18699
+ { originalError: error }
18700
+ );
18701
+ }
18702
+ logger.debug("Task created", {
18703
+ taskId,
18704
+ type: validated.type,
18705
+ payloadSize,
18706
+ compressedSize: payloadBuffer.length,
18707
+ compressionRatio: compressionRatio.toFixed(2)
18708
+ });
18709
+ return {
18710
+ id: taskId,
18711
+ status: "pending",
18712
+ estimatedEngine: estimatedEngine ?? null,
18713
+ expiresAt,
18714
+ payloadSize,
18715
+ compressionRatio
18716
+ };
18717
+ }
18718
+ /**
18719
+ * Get a task by ID
18720
+ */
18721
+ getTask(taskId) {
18722
+ this.ensureOpen();
18723
+ const row = this.stmtGetById.get(taskId);
18724
+ if (!row) {
18725
+ return null;
18726
+ }
18727
+ return this.rowToTask(row);
18728
+ }
18729
+ /**
18730
+ * Update task status
18731
+ */
18732
+ updateTaskStatus(taskId, status, error) {
18733
+ this.ensureOpen();
18734
+ const now = Date.now();
18735
+ const params = {
18736
+ id: taskId,
18737
+ status,
18738
+ started_at: status === "running" ? now : null,
18739
+ completed_at: status === "completed" || status === "failed" ? now : null,
18740
+ error_code: error?.code ?? null,
18741
+ error_message: error?.message ?? null
18742
+ };
18743
+ const result = this.stmtUpdateStatus.run(params);
18744
+ if (result.changes === 0) {
18745
+ throw new TaskEngineError(
18746
+ `Task not found: ${taskId}`,
18747
+ "TASK_NOT_FOUND"
18748
+ );
18749
+ }
18750
+ logger.debug("Task status updated", { taskId, status });
18751
+ }
18752
+ /**
18753
+ * Update task with successful result
18754
+ */
18755
+ updateTaskResult(taskId, result, metrics) {
18756
+ this.ensureOpen();
18757
+ const now = Date.now();
18758
+ const resultJson = JSON.stringify(result);
18759
+ const resultSize = Buffer.byteLength(resultJson, "utf-8");
18760
+ let resultBuffer;
18761
+ let resultIsCompressed;
18762
+ if (this.config.compressionEnabled) {
18763
+ const compressed = compressWithInfo(result, {
18764
+ level: this.config.compressionLevel
18765
+ });
18766
+ resultBuffer = compressed.data;
18767
+ resultIsCompressed = compressed.compressed;
18768
+ } else {
18769
+ resultBuffer = Buffer.from(resultJson, "utf-8");
18770
+ resultIsCompressed = false;
18771
+ }
18772
+ const dbResult = this.stmtUpdateResult.run({
18773
+ id: taskId,
18774
+ result_compressed: resultBuffer,
18775
+ result_size_bytes: resultSize,
18776
+ result_is_compressed: resultIsCompressed ? 1 : 0,
18777
+ completed_at: now,
18778
+ duration_ms: metrics.durationMs ?? null,
18779
+ tokens_prompt: metrics.tokensPrompt ?? null,
18780
+ tokens_completion: metrics.tokensCompletion ?? null
18781
+ });
18782
+ if (dbResult.changes === 0) {
18783
+ throw new TaskEngineError(
18784
+ `Task not found: ${taskId}`,
18785
+ "TASK_NOT_FOUND"
18786
+ );
18787
+ }
18788
+ logger.debug("Task result updated", {
18789
+ taskId,
18790
+ resultSize,
18791
+ durationMs: metrics.durationMs
18792
+ });
18793
+ }
18794
+ /**
18795
+ * Update task with failure
18796
+ */
18797
+ updateTaskFailed(taskId, error, durationMs) {
18798
+ this.ensureOpen();
18799
+ const now = Date.now();
18800
+ const result = this.stmtUpdateFailed.run({
18801
+ id: taskId,
18802
+ completed_at: now,
18803
+ duration_ms: durationMs ?? null,
18804
+ error_code: error.code,
18805
+ error_message: error.message
18806
+ });
18807
+ if (result.changes === 0) {
18808
+ throw new TaskEngineError(
18809
+ `Task not found: ${taskId}`,
18810
+ "TASK_NOT_FOUND"
18811
+ );
18812
+ }
18813
+ logger.debug("Task marked as failed", { taskId, error: error.code });
18814
+ }
18815
+ /**
18816
+ * Increment retry count
18817
+ */
18818
+ incrementRetry(taskId) {
18819
+ this.ensureOpen();
18820
+ this.stmtIncrementRetry.run(taskId);
18821
+ }
18822
+ /**
18823
+ * Delete a task
18824
+ */
18825
+ deleteTask(taskId) {
18826
+ this.ensureOpen();
18827
+ const result = this.stmtDelete.run(taskId);
18828
+ const deleted = result.changes > 0;
18829
+ if (deleted) {
18830
+ logger.debug("Task deleted", { taskId });
18831
+ }
18832
+ return deleted;
18833
+ }
18834
+ /**
18835
+ * List tasks with optional filtering
18836
+ */
18837
+ listTasks(filter = {}) {
18838
+ this.ensureOpen();
18839
+ const validated = TaskFilterSchema.parse(filter);
18840
+ const conditions = ["1=1"];
18841
+ const params = {};
18842
+ if (validated.status) {
18843
+ conditions.push("status = :status");
18844
+ params.status = validated.status;
18845
+ }
18846
+ if (validated.engine) {
18847
+ conditions.push("engine = :engine");
18848
+ params.engine = validated.engine;
18849
+ }
18850
+ if (validated.type) {
18851
+ conditions.push("type = :type");
18852
+ params.type = validated.type;
18853
+ }
18854
+ if (validated.originClient) {
18855
+ conditions.push("origin_client = :origin_client");
18856
+ params.origin_client = validated.originClient;
18857
+ }
18858
+ const query = `
18859
+ SELECT * FROM task_store
18860
+ WHERE ${conditions.join(" AND ")}
18861
+ ORDER BY priority DESC, created_at ASC
18862
+ LIMIT :limit OFFSET :offset
18863
+ `;
18864
+ params.limit = validated.limit ?? 20;
18865
+ params.offset = validated.offset ?? 0;
18866
+ const stmt = this.db.prepare(query);
18867
+ const rows = stmt.all(params);
18868
+ return rows.map((row) => this.rowToTask(row));
18869
+ }
18870
+ /**
18871
+ * Count tasks by status
18872
+ */
18873
+ countByStatus() {
18874
+ this.ensureOpen();
18875
+ const rows = this.stmtCountByStatus.all();
18876
+ const counts = {
18877
+ pending: 0,
18878
+ running: 0,
18879
+ completed: 0,
18880
+ failed: 0,
18881
+ expired: 0
18882
+ };
18883
+ for (const row of rows) {
18884
+ counts[row.status] = row.count;
18885
+ }
18886
+ return counts;
18887
+ }
18888
+ /**
18889
+ * Count tasks matching filter (for pagination)
18890
+ */
18891
+ countTasks(filter = {}) {
18892
+ this.ensureOpen();
18893
+ const validated = TaskFilterSchema.parse(filter);
18894
+ const conditions = ["1=1"];
18895
+ const params = {};
18896
+ if (validated.status) {
18897
+ conditions.push("status = :status");
18898
+ params.status = validated.status;
18899
+ }
18900
+ if (validated.engine) {
18901
+ conditions.push("engine = :engine");
18902
+ params.engine = validated.engine;
18903
+ }
18904
+ if (validated.type) {
18905
+ conditions.push("type = :type");
18906
+ params.type = validated.type;
18907
+ }
18908
+ if (validated.originClient) {
18909
+ conditions.push("origin_client = :origin_client");
18910
+ params.origin_client = validated.originClient;
18911
+ }
18912
+ const query = `SELECT COUNT(*) as count FROM task_store WHERE ${conditions.join(" AND ")}`;
18913
+ const stmt = this.db.prepare(query);
18914
+ const row = stmt.get(params);
18915
+ return row.count;
18916
+ }
18917
+ /**
18918
+ * Find a completed task with the same payload (for caching)
18919
+ */
18920
+ findByPayloadHash(payloadHash) {
18921
+ this.ensureOpen();
18922
+ const row = this.stmtFindByHash.get(payloadHash);
18923
+ return row?.id ?? null;
18924
+ }
18925
+ /**
18926
+ * Cleanup expired tasks
18927
+ */
18928
+ cleanupExpired() {
18929
+ this.ensureOpen();
18930
+ const now = Date.now();
18931
+ const result = this.stmtCleanup.run(now);
18932
+ if (result.changes > 0) {
18933
+ logger.debug("Expired tasks cleaned up", { count: result.changes });
18934
+ }
18935
+ return result.changes;
18936
+ }
18937
+ /**
18938
+ * Mark zombie running tasks as failed.
18939
+ * These are tasks that have been in 'running' state past their expiry time,
18940
+ * likely due to process crashes or other failures.
18941
+ */
18942
+ cleanupZombieRunning() {
18943
+ this.ensureOpen();
18944
+ const now = Date.now();
18945
+ const result = this.stmtCleanupZombies.run({
18946
+ now,
18947
+ completed_at: now
18948
+ });
18949
+ if (result.changes > 0) {
18950
+ logger.warn("Zombie running tasks marked as failed", { count: result.changes });
18951
+ }
18952
+ return result.changes;
18953
+ }
18954
+ /**
18955
+ * Full cleanup: handles both expired non-running tasks and zombie running tasks.
18956
+ * Call this periodically to prevent resource leaks.
18957
+ */
18958
+ cleanupAll() {
18959
+ const zombies = this.cleanupZombieRunning();
18960
+ const expired = this.cleanupExpired();
18961
+ return { expired, zombies };
18962
+ }
18963
+ /**
18964
+ * Get store statistics
18965
+ */
18966
+ getStats() {
18967
+ this.ensureOpen();
18968
+ const counts = this.countByStatus();
18969
+ const totalTasks = Object.values(counts).reduce((a, b) => a + b, 0);
18970
+ let dbSizeBytes = 0;
18971
+ try {
18972
+ const pageCount = this.db.pragma("page_count", { simple: true });
18973
+ const pageSize = this.db.pragma("page_size", { simple: true });
18974
+ dbSizeBytes = pageCount * pageSize;
18975
+ } catch {
18976
+ }
18977
+ return {
18978
+ totalTasks,
18979
+ byStatus: counts,
18980
+ dbSizeBytes
18981
+ };
18982
+ }
18983
+ /**
18984
+ * Close the store
18985
+ */
18986
+ close() {
18987
+ if (this.closed) return;
18988
+ DatabaseFactory.close(this.db);
18989
+ this.closed = true;
18990
+ logger.debug("TaskStore closed");
18991
+ }
18992
+ // ============================================================================
18993
+ // Private Methods
18994
+ // ============================================================================
18995
+ initializeSchema() {
18996
+ this.db.exec(SQL.CREATE_TABLE);
18997
+ const indexStatements = SQL.CREATE_INDEXES.split(";").filter((s) => s.trim());
18998
+ for (const stmt of indexStatements) {
18999
+ this.db.exec(stmt);
19000
+ }
19001
+ }
19002
+ prepareStatements() {
19003
+ this.stmtInsert = this.db.prepare(SQL.INSERT_TASK);
19004
+ this.stmtGetById = this.db.prepare(SQL.GET_BY_ID);
19005
+ this.stmtUpdateStatus = this.db.prepare(SQL.UPDATE_STATUS);
19006
+ this.stmtUpdateResult = this.db.prepare(SQL.UPDATE_RESULT);
19007
+ this.stmtUpdateFailed = this.db.prepare(SQL.UPDATE_FAILED);
19008
+ this.stmtIncrementRetry = this.db.prepare(SQL.INCREMENT_RETRY);
19009
+ this.stmtDelete = this.db.prepare(SQL.DELETE_TASK);
19010
+ this.stmtCleanup = this.db.prepare(SQL.CLEANUP_EXPIRED);
19011
+ this.stmtCleanupZombies = this.db.prepare(SQL.CLEANUP_ZOMBIE_RUNNING);
19012
+ this.stmtCountByStatus = this.db.prepare(SQL.COUNT_BY_STATUS);
19013
+ this.stmtFindByHash = this.db.prepare(SQL.FIND_BY_HASH);
19014
+ }
19015
+ ensureOpen() {
19016
+ if (this.closed) {
19017
+ throw new TaskEngineError("TaskStore is closed", "STORE_ERROR");
19018
+ }
19019
+ }
19020
+ generateTaskId() {
19021
+ const timestamp = Date.now().toString(36);
19022
+ const random = randomBytes(4).toString("hex");
19023
+ return `task_${timestamp}${random}`;
19024
+ }
19025
+ hashPayload(json) {
19026
+ return createHash("sha256").update(json).digest("hex").substring(0, 16);
19027
+ }
19028
+ estimateEngine(type) {
19029
+ switch (type) {
19030
+ case "web_search":
19031
+ return "gemini";
19032
+ // Gemini is good for web search
19033
+ case "code_review":
19034
+ case "code_generation":
19035
+ return "claude";
19036
+ // Claude is good for code
19037
+ case "analysis":
19038
+ return "claude";
19039
+ // Claude is good for analysis
19040
+ case "custom":
19041
+ default:
19042
+ return "auto";
19043
+ }
19044
+ }
19045
+ rowToTask(row) {
19046
+ let payload;
19047
+ try {
19048
+ if (row.is_compressed) {
19049
+ payload = decompressPayload(row.payload_compressed);
19050
+ } else {
19051
+ payload = JSON.parse(row.payload_compressed.toString("utf-8"));
19052
+ }
19053
+ } catch (error) {
19054
+ logger.error("Failed to decompress task payload", {
19055
+ taskId: row.id,
19056
+ error: error instanceof Error ? error.message : String(error)
19057
+ });
19058
+ throw new TaskEngineError(
19059
+ `Failed to decompress task payload for task ${row.id}: ${error instanceof Error ? error.message : "Unknown error"}`,
19060
+ "STORE_ERROR",
19061
+ { taskId: row.id, originalError: error }
19062
+ );
19063
+ }
19064
+ let result = null;
19065
+ if (row.result_compressed) {
19066
+ try {
19067
+ if (row.result_is_compressed) {
19068
+ result = decompressPayload(row.result_compressed);
19069
+ } else {
19070
+ result = JSON.parse(row.result_compressed.toString("utf-8"));
19071
+ }
19072
+ } catch (error) {
19073
+ logger.warn("Failed to decompress task result, returning null", {
19074
+ taskId: row.id,
19075
+ error: error instanceof Error ? error.message : String(error)
19076
+ });
19077
+ result = null;
19078
+ }
19079
+ }
19080
+ return {
19081
+ id: row.id,
19082
+ type: row.type,
19083
+ status: row.status,
19084
+ engine: row.engine,
19085
+ priority: row.priority,
19086
+ payload,
19087
+ payloadSize: row.payload_size_bytes,
19088
+ result,
19089
+ context: {
19090
+ originClient: row.origin_client,
19091
+ callChain: JSON.parse(row.call_chain),
19092
+ depth: row.depth
19093
+ },
19094
+ createdAt: row.created_at,
19095
+ startedAt: row.started_at,
19096
+ completedAt: row.completed_at,
19097
+ expiresAt: row.expires_at,
19098
+ metrics: row.duration_ms != null ? {
19099
+ durationMs: row.duration_ms,
19100
+ tokensPrompt: row.tokens_prompt,
19101
+ tokensCompletion: row.tokens_completion
19102
+ } : null,
19103
+ error: row.error_code ? {
19104
+ code: row.error_code,
19105
+ message: row.error_message ?? ""
19106
+ } : null,
19107
+ retryCount: row.retry_count
19108
+ };
19109
+ }
19110
+ };
19111
+ function createTaskStore(config) {
19112
+ return new TaskStore(config);
19113
+ }
19114
+
19115
+ // src/core/task-engine/engine.ts
19116
+ init_esm_shims();
19117
+ init_logger();
19118
+ var DEFAULT_CONFIG4 = {
19119
+ store: {},
19120
+ loopGuard: {},
19121
+ maxConcurrent: 50,
19122
+ // Phase 5: Increased from 36 to 50
19123
+ defaultTimeoutMs: 12e4,
19124
+ maxRetries: 3,
19125
+ retryDelayMs: 1e3,
19126
+ cacheEnabled: true,
19127
+ cacheTtlMs: 36e5
19128
+ // 1 hour
19129
+ };
19130
+ var TaskEngine = class extends EventEmitter {
19131
+ config;
19132
+ loopGuard;
19133
+ store;
19134
+ runningTasks = /* @__PURE__ */ new Map();
19135
+ closed = false;
19136
+ // Statistics
19137
+ stats = {
19138
+ totalCreated: 0,
19139
+ completed: 0,
19140
+ failed: 0,
19141
+ expired: 0,
19142
+ cacheHits: 0,
19143
+ cacheMisses: 0,
19144
+ totalDurationMs: 0
19145
+ };
19146
+ constructor(config = {}) {
19147
+ super();
19148
+ this.config = {
19149
+ ...DEFAULT_CONFIG4,
19150
+ ...config,
19151
+ store: { ...DEFAULT_CONFIG4.store, ...config.store },
19152
+ loopGuard: { ...DEFAULT_CONFIG4.loopGuard, ...config.loopGuard }
19153
+ };
19154
+ this.loopGuard = createLoopGuard(this.config.loopGuard);
19155
+ this.store = createTaskStore(this.config.store);
19156
+ logger.info("TaskEngine initialized", {
19157
+ maxConcurrent: this.config.maxConcurrent,
19158
+ defaultTimeoutMs: this.config.defaultTimeoutMs,
19159
+ cacheEnabled: this.config.cacheEnabled
19160
+ });
19161
+ }
19162
+ // ============================================================================
19163
+ // Public API
19164
+ // ============================================================================
19165
+ /**
19166
+ * Create a new task
19167
+ */
19168
+ async createTask(input) {
19169
+ this.ensureOpen();
19170
+ try {
19171
+ const result = this.store.createTask(input);
19172
+ this.stats.totalCreated++;
19173
+ this.emit("task:created", result);
19174
+ logger.debug("Task created via engine", { taskId: result.id });
19175
+ return result;
19176
+ } catch (error) {
19177
+ if (error instanceof TaskEngineError) {
19178
+ throw error;
19179
+ }
19180
+ throw new TaskEngineError(
19181
+ `Failed to create task: ${error instanceof Error ? error.message : "Unknown error"}`,
19182
+ "STORE_ERROR",
19183
+ { originalError: error }
19184
+ );
19185
+ }
19186
+ }
19187
+ /**
19188
+ * Run a task
19189
+ */
19190
+ async runTask(taskId, options = {}) {
19191
+ this.ensureOpen();
19192
+ if (this.runningTasks.size >= this.config.maxConcurrent) {
19193
+ throw new TaskEngineError(
19194
+ `Maximum concurrent tasks reached: ${this.config.maxConcurrent}`,
19195
+ "EXECUTION_FAILED",
19196
+ { runningCount: this.runningTasks.size, limit: this.config.maxConcurrent }
19197
+ );
19198
+ }
19199
+ const task = this.store.getTask(taskId);
19200
+ if (!task) {
19201
+ throw new TaskEngineError(`Task not found: ${taskId}`, "TASK_NOT_FOUND");
19202
+ }
19203
+ if (task.status === "running") {
19204
+ throw new TaskEngineError(
19205
+ `Task is already running: ${taskId}`,
19206
+ "TASK_ALREADY_RUNNING"
19207
+ );
19208
+ }
19209
+ if (task.status === "completed") {
19210
+ if (task.result) {
19211
+ this.stats.cacheHits++;
19212
+ return {
19213
+ taskId,
19214
+ status: "completed",
19215
+ result: task.result,
19216
+ engine: task.engine ?? "auto",
19217
+ metrics: task.metrics,
19218
+ error: null,
19219
+ cacheHit: true
19220
+ };
19221
+ }
19222
+ }
19223
+ if (task.status === "expired") {
19224
+ throw new TaskEngineError(`Task has expired: ${taskId}`, "TASK_EXPIRED");
19225
+ }
19226
+ if (Date.now() > task.expiresAt) {
19227
+ this.store.updateTaskStatus(taskId, "expired");
19228
+ throw new TaskEngineError(`Task has expired: ${taskId}`, "TASK_EXPIRED");
19229
+ }
19230
+ const targetEngine = options.engineOverride ?? task.engine ?? this.estimateEngine(task.type);
19231
+ const loopContext = this.buildLoopContext(task, options.loopContext);
19232
+ try {
19233
+ this.loopGuard.validateExecution(loopContext, targetEngine);
19234
+ } catch (error) {
19235
+ if (isLoopPreventionError(error)) {
19236
+ this.emit("loop:prevented", taskId, loopContext, targetEngine);
19237
+ logger.warn("Loop prevented", {
19238
+ taskId,
19239
+ targetEngine,
19240
+ callChain: error.callChain
19241
+ });
19242
+ }
19243
+ throw error;
19244
+ }
19245
+ return this.executeTask(task, targetEngine, loopContext, options);
19246
+ }
19247
+ /**
19248
+ * Get task result (without running)
19249
+ */
19250
+ getTaskResult(taskId) {
19251
+ this.ensureOpen();
19252
+ const task = this.store.getTask(taskId);
19253
+ if (!task) {
19254
+ return null;
19255
+ }
19256
+ if (task.status !== "completed") {
19257
+ return null;
19258
+ }
19259
+ return {
19260
+ taskId,
19261
+ status: "completed",
19262
+ result: task.result,
19263
+ engine: task.engine ?? "auto",
19264
+ metrics: task.metrics,
19265
+ error: null,
19266
+ cacheHit: true
19267
+ };
19268
+ }
19269
+ /**
19270
+ * Get a task by ID
19271
+ */
19272
+ getTask(taskId) {
19273
+ this.ensureOpen();
19274
+ return this.store.getTask(taskId);
19275
+ }
19276
+ /**
19277
+ * List tasks with pagination info
19278
+ */
19279
+ listTasks(filter) {
19280
+ this.ensureOpen();
19281
+ const tasks = this.store.listTasks(filter);
19282
+ const total = this.store.countTasks(filter);
19283
+ return { tasks, total };
19284
+ }
19285
+ /**
19286
+ * Delete a task
19287
+ */
19288
+ deleteTask(taskId, force = false) {
19289
+ this.ensureOpen();
19290
+ const task = this.store.getTask(taskId);
19291
+ if (!task) {
19292
+ return false;
19293
+ }
19294
+ if (task.status === "running" && !force) {
19295
+ throw new TaskEngineError(
19296
+ `Cannot delete running task: ${taskId}. Use force=true to override.`,
19297
+ "TASK_ALREADY_RUNNING"
19298
+ );
19299
+ }
19300
+ if (task.status === "running") {
19301
+ const controller = this.runningTasks.get(taskId);
19302
+ if (controller) {
19303
+ controller.abort();
19304
+ this.runningTasks.delete(taskId);
19305
+ }
19306
+ }
19307
+ return this.store.deleteTask(taskId);
19308
+ }
19309
+ /**
19310
+ * Get engine statistics
19311
+ */
19312
+ getStats() {
19313
+ this.ensureOpen();
19314
+ const storeStats = this.store.getStats();
19315
+ const cacheTotal = this.stats.cacheHits + this.stats.cacheMisses;
19316
+ return {
19317
+ totalCreated: this.stats.totalCreated,
19318
+ runningCount: this.runningTasks.size,
19319
+ completedCount: storeStats.byStatus.completed,
19320
+ failedCount: storeStats.byStatus.failed,
19321
+ expiredCount: storeStats.byStatus.expired,
19322
+ cache: {
19323
+ hits: this.stats.cacheHits,
19324
+ misses: this.stats.cacheMisses,
19325
+ hitRate: cacheTotal > 0 ? this.stats.cacheHits / cacheTotal : 0
19326
+ },
19327
+ avgDurationMs: this.stats.completed > 0 ? this.stats.totalDurationMs / this.stats.completed : 0
19328
+ };
19329
+ }
19330
+ /**
19331
+ * Cleanup expired tasks
19332
+ */
19333
+ cleanupExpired() {
19334
+ this.ensureOpen();
19335
+ const cleaned = this.store.cleanupExpired();
19336
+ this.stats.expired += cleaned;
19337
+ return cleaned;
19338
+ }
19339
+ /**
19340
+ * Shutdown the engine
19341
+ */
19342
+ async shutdown() {
19343
+ if (this.closed) return;
19344
+ logger.info("TaskEngine shutting down", {
19345
+ runningTasks: this.runningTasks.size
19346
+ });
19347
+ for (const [taskId, controller] of this.runningTasks) {
19348
+ controller.abort();
19349
+ logger.debug("Cancelled running task", { taskId });
19350
+ }
19351
+ this.runningTasks.clear();
19352
+ this.store.close();
19353
+ this.closed = true;
19354
+ logger.info("TaskEngine shutdown complete");
19355
+ }
19356
+ /**
19357
+ * Check if engine is healthy
19358
+ */
19359
+ isHealthy() {
19360
+ return !this.closed && this.runningTasks.size < this.config.maxConcurrent;
19361
+ }
19362
+ // ============================================================================
19363
+ // Private Methods
19364
+ // ============================================================================
19365
+ async executeTask(task, targetEngine, context, options) {
19366
+ const taskId = task.id;
19367
+ const abortController = new AbortController();
19368
+ const startTime = Date.now();
19369
+ this.runningTasks.set(taskId, abortController);
19370
+ this.store.updateTaskStatus(taskId, "running");
19371
+ this.emit("task:started", taskId, targetEngine);
19372
+ const timeoutMs = options.timeoutMs ?? this.config.defaultTimeoutMs;
19373
+ const timeoutId = setTimeout(() => {
19374
+ abortController.abort();
19375
+ }, timeoutMs);
19376
+ try {
19377
+ const result = await this.executeWithRetry(
19378
+ task,
19379
+ targetEngine,
19380
+ context,
19381
+ abortController.signal
19382
+ );
19383
+ const durationMs = Date.now() - startTime;
19384
+ this.store.updateTaskResult(taskId, result, {
19385
+ durationMs,
19386
+ tokensPrompt: null,
19387
+ tokensCompletion: null
19388
+ });
19389
+ this.stats.completed++;
19390
+ this.stats.cacheMisses++;
19391
+ this.stats.totalDurationMs += durationMs;
19392
+ const taskResult = {
19393
+ taskId,
19394
+ status: "completed",
19395
+ result,
19396
+ engine: targetEngine,
19397
+ metrics: { durationMs, tokensPrompt: null, tokensCompletion: null },
19398
+ error: null,
19399
+ cacheHit: false
19400
+ };
19401
+ this.emit("task:completed", taskId, taskResult);
19402
+ logger.debug("Task completed", { taskId, durationMs, engine: targetEngine });
19403
+ return taskResult;
19404
+ } catch (error) {
19405
+ const durationMs = Date.now() - startTime;
19406
+ const errorObj = error instanceof Error ? error : new Error(String(error));
19407
+ if (abortController.signal.aborted) {
19408
+ const taskError2 = { code: "TIMEOUT", message: `Task timed out after ${timeoutMs}ms` };
19409
+ this.store.updateTaskFailed(taskId, taskError2, durationMs);
19410
+ this.stats.failed++;
19411
+ this.emit("task:failed", taskId, new TaskEngineError(taskError2.message, "EXECUTION_TIMEOUT"));
19412
+ return {
19413
+ taskId,
19414
+ status: "failed",
19415
+ result: null,
19416
+ engine: targetEngine,
19417
+ metrics: { durationMs, tokensPrompt: null, tokensCompletion: null },
19418
+ error: taskError2,
19419
+ cacheHit: false
19420
+ };
19421
+ }
19422
+ const taskError = {
19423
+ code: error instanceof TaskEngineError ? error.code : "EXECUTION_FAILED",
19424
+ message: errorObj.message
19425
+ };
19426
+ this.store.updateTaskFailed(taskId, taskError, durationMs);
19427
+ this.stats.failed++;
19428
+ this.emit("task:failed", taskId, errorObj);
19429
+ logger.error("Task failed", { taskId, error: errorObj.message, durationMs });
19430
+ return {
19431
+ taskId,
19432
+ status: "failed",
19433
+ result: null,
19434
+ engine: targetEngine,
19435
+ metrics: { durationMs, tokensPrompt: null, tokensCompletion: null },
19436
+ error: taskError,
19437
+ cacheHit: false
19438
+ };
19439
+ } finally {
19440
+ clearTimeout(timeoutId);
19441
+ this.runningTasks.delete(taskId);
19442
+ }
19443
+ }
19444
+ async executeWithRetry(task, engine, context, signal) {
19445
+ let lastError;
19446
+ for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
19447
+ if (signal.aborted) {
19448
+ throw new TaskEngineError("Task execution aborted", "EXECUTION_TIMEOUT");
19449
+ }
19450
+ try {
19451
+ return await this.executeOnEngine(task, engine, context, signal);
19452
+ } catch (error) {
19453
+ lastError = error instanceof Error ? error : new Error(String(error));
19454
+ if (isLoopPreventionError(error)) {
19455
+ throw error;
19456
+ }
19457
+ if (signal.aborted) {
19458
+ throw error;
19459
+ }
19460
+ if (attempt < this.config.maxRetries && this.isRetryableError(error)) {
19461
+ const delay = this.config.retryDelayMs * Math.pow(2, attempt);
19462
+ this.store.incrementRetry(task.id);
19463
+ this.emit("task:retry", task.id, attempt + 1, lastError);
19464
+ logger.debug("Retrying task", {
19465
+ taskId: task.id,
19466
+ attempt: attempt + 1,
19467
+ delay,
19468
+ error: lastError.message
19469
+ });
19470
+ await this.sleep(delay, signal);
19471
+ continue;
19472
+ }
19473
+ throw error;
19474
+ }
19475
+ }
19476
+ throw lastError ?? new TaskEngineError("Task execution failed", "EXECUTION_FAILED");
19477
+ }
19478
+ /**
19479
+ * Execute task on target engine
19480
+ *
19481
+ * Phase 1: Simulated execution (returns mock result)
19482
+ * Phase 2+: Will integrate with Router for actual execution
19483
+ */
19484
+ async executeOnEngine(task, engine, context, signal) {
19485
+ logger.debug("Executing task on engine", {
19486
+ taskId: task.id,
19487
+ engine,
19488
+ type: task.type,
19489
+ callChain: context.callChain
19490
+ });
19491
+ await this.sleep(100, signal);
19492
+ switch (task.type) {
19493
+ case "web_search":
19494
+ return {
19495
+ query: task.payload.query ?? "unknown",
19496
+ results: [
19497
+ { title: "Result 1", url: "https://example.com/1", snippet: "Sample result 1" },
19498
+ { title: "Result 2", url: "https://example.com/2", snippet: "Sample result 2" }
19499
+ ],
19500
+ engine,
19501
+ timestamp: Date.now()
19502
+ };
19503
+ case "code_review":
19504
+ return {
19505
+ file: task.payload.file ?? "unknown.ts",
19506
+ issues: [],
19507
+ suggestions: ["Consider adding type annotations"],
19508
+ engine,
19509
+ timestamp: Date.now()
19510
+ };
19511
+ case "code_generation":
19512
+ return {
19513
+ prompt: task.payload.prompt ?? "",
19514
+ code: "// Generated code placeholder",
19515
+ language: task.payload.language ?? "typescript",
19516
+ engine,
19517
+ timestamp: Date.now()
19518
+ };
19519
+ case "analysis":
19520
+ return {
19521
+ input: task.payload.input ?? {},
19522
+ analysis: "Analysis result placeholder",
19523
+ confidence: 0.95,
19524
+ engine,
19525
+ timestamp: Date.now()
19526
+ };
19527
+ case "custom":
19528
+ default:
19529
+ return {
19530
+ payload: task.payload,
19531
+ result: "Custom task completed",
19532
+ engine,
19533
+ timestamp: Date.now()
19534
+ };
19535
+ }
19536
+ }
19537
+ buildLoopContext(task, incomingContext) {
19538
+ if (incomingContext) {
19539
+ return this.loopGuard.mergeContext(incomingContext);
19540
+ }
19541
+ return {
19542
+ taskId: task.id,
19543
+ originClient: task.context.originClient,
19544
+ callChain: task.context.callChain.length > 0 ? task.context.callChain : [task.context.originClient],
19545
+ depth: task.context.depth,
19546
+ maxDepth: this.loopGuard.getConfig().maxDepth,
19547
+ createdAt: task.createdAt
19548
+ };
19549
+ }
19550
+ estimateEngine(type) {
19551
+ switch (type) {
19552
+ case "web_search":
19553
+ return "gemini";
19554
+ case "code_review":
19555
+ case "code_generation":
19556
+ return "claude";
19557
+ case "analysis":
19558
+ return "claude";
19559
+ default:
19560
+ return "claude";
19561
+ }
19562
+ }
19563
+ isRetryableError(error) {
19564
+ if (error instanceof TaskEngineError) {
19565
+ const nonRetryable = [
19566
+ "TASK_NOT_FOUND",
19567
+ "TASK_EXPIRED",
19568
+ "PAYLOAD_TOO_LARGE",
19569
+ "LOOP_DETECTED",
19570
+ "DEPTH_EXCEEDED",
19571
+ "CHAIN_TOO_LONG",
19572
+ "BLOCKED_PATTERN"
19573
+ ];
19574
+ return !nonRetryable.includes(error.code);
19575
+ }
19576
+ return true;
19577
+ }
19578
+ ensureOpen() {
19579
+ if (this.closed) {
19580
+ throw new TaskEngineError("TaskEngine is closed", "EXECUTION_FAILED");
19581
+ }
19582
+ }
19583
+ sleep(ms, signal) {
19584
+ return new Promise((resolve5, reject) => {
19585
+ if (signal?.aborted) {
19586
+ reject(new Error("Aborted"));
19587
+ return;
19588
+ }
19589
+ const abortHandler = () => {
19590
+ clearTimeout(timeoutId);
19591
+ reject(new Error("Aborted"));
19592
+ };
19593
+ const timeoutId = setTimeout(() => {
19594
+ signal?.removeEventListener("abort", abortHandler);
19595
+ resolve5();
19596
+ }, ms);
19597
+ signal?.addEventListener("abort", abortHandler, { once: true });
19598
+ });
19599
+ }
19600
+ };
19601
+ var defaultTaskEngine = null;
19602
+ function getTaskEngine(config) {
19603
+ if (!defaultTaskEngine) {
19604
+ defaultTaskEngine = new TaskEngine(config);
19605
+ }
19606
+ return defaultTaskEngine;
19607
+ }
19608
+
19609
+ // src/core/task-engine/cache.ts
19610
+ init_esm_shims();
19611
+ init_logger();
19612
+
19613
+ // src/core/task-engine/write-batcher.ts
19614
+ init_esm_shims();
19615
+ init_logger();
19616
+
19617
+ // src/core/task-engine/request-coalescer.ts
19618
+ init_esm_shims();
19619
+ init_logger();
19620
+
19621
+ // src/core/task-engine/rate-limiter.ts
19622
+ init_esm_shims();
19623
+ init_logger();
19624
+
19625
+ // src/core/task-engine/audit-logger.ts
19626
+ init_esm_shims();
19627
+ init_logger();
19628
+
19629
+ // src/core/task-engine/circuit-breaker.ts
19630
+ init_esm_shims();
19631
+ init_logger();
19632
+
19633
+ // src/core/task-engine/write-pool.ts
19634
+ init_esm_shims();
19635
+ init_logger();
19636
+
19637
+ // src/core/task-engine/task-queue.ts
19638
+ init_esm_shims();
19639
+ init_logger();
19640
+
19641
+ // src/mcp/tools/task/create-task.ts
19642
+ init_logger();
19643
+ function createCreateTaskHandler(deps) {
19644
+ return async (input) => {
19645
+ const startTime = Date.now();
19646
+ logger.info("[create_task] Creating task", {
19647
+ type: input.type,
19648
+ engine: input.engine ?? "auto",
19649
+ priority: input.priority ?? 5,
19650
+ payloadSize: JSON.stringify(input.payload).length
19651
+ });
19652
+ try {
19653
+ const taskEngine = getTaskEngine();
19654
+ const session = deps.getSession();
19655
+ const taskInput = {
19656
+ type: input.type,
19657
+ payload: input.payload,
19658
+ engine: input.engine,
19659
+ priority: input.priority,
19660
+ ttlHours: input.ttl_hours,
19661
+ context: session ? {
19662
+ originClient: mapNormalizedProviderToOriginClient(session.normalizedProvider),
19663
+ callChain: [mapNormalizedProviderToOriginClient(session.normalizedProvider)],
19664
+ depth: 0
19665
+ } : void 0
19666
+ };
19667
+ const result = await taskEngine.createTask(taskInput);
19668
+ const output = {
19669
+ task_id: result.id,
19670
+ status: "pending",
19671
+ estimated_engine: result.estimatedEngine,
19672
+ expires_at: new Date(result.expiresAt).toISOString(),
19673
+ payload_size_bytes: result.payloadSize,
19674
+ compression_ratio: result.compressionRatio
19675
+ };
19676
+ logger.info("[create_task] Task created successfully", {
19677
+ taskId: result.id,
19678
+ estimatedEngine: result.estimatedEngine,
19679
+ durationMs: Date.now() - startTime
19680
+ });
19681
+ return output;
19682
+ } catch (error) {
19683
+ logger.error("[create_task] Failed to create task", {
19684
+ error: error instanceof Error ? error.message : String(error),
19685
+ type: input.type
19686
+ });
19687
+ throw error;
19688
+ }
19689
+ };
19690
+ }
19691
+ function mapNormalizedProviderToOriginClient(provider) {
19692
+ const mapping = {
19693
+ "claude": "claude-code",
19694
+ "gemini": "gemini-cli",
19695
+ "codex": "codex-cli",
19696
+ "ax-cli": "ax-cli",
19697
+ "unknown": "unknown"
19698
+ };
19699
+ return mapping[provider] ?? "unknown";
19700
+ }
19701
+ var createTaskSchema = {
19702
+ name: "create_task",
19703
+ description: "Create a new task with payload for deferred execution. Returns task_id for later execution via run_task.",
19704
+ inputSchema: {
19705
+ type: "object",
19706
+ properties: {
19707
+ type: {
19708
+ type: "string",
19709
+ enum: ["web_search", "code_review", "code_generation", "analysis", "custom"],
19710
+ description: "Task type for routing optimization"
19711
+ },
19712
+ payload: {
19713
+ type: "object",
19714
+ description: "Task data (max 1MB after JSON serialization)"
19715
+ },
19716
+ engine: {
19717
+ type: "string",
19718
+ enum: ["auto", "gemini", "claude", "codex", "ax-cli"],
19719
+ default: "auto",
19720
+ description: "Target engine (auto = router decides)"
19721
+ },
19722
+ priority: {
19723
+ type: "integer",
19724
+ minimum: 1,
19725
+ maximum: 10,
19726
+ default: 5,
19727
+ description: "Execution priority (1=lowest, 10=highest)"
19728
+ },
19729
+ ttl_hours: {
19730
+ type: "integer",
19731
+ minimum: 1,
19732
+ maximum: 168,
19733
+ default: 24,
19734
+ description: "Task time-to-live in hours"
19735
+ }
19736
+ },
19737
+ required: ["type", "payload"]
19738
+ }
19739
+ };
19740
+
19741
+ // src/mcp/tools/task/run-task.ts
19742
+ init_esm_shims();
19743
+ init_logger();
19744
+ function createRunTaskHandler(deps) {
19745
+ return async (input) => {
19746
+ const startTime = Date.now();
19747
+ logger.info("[run_task] Executing task", {
19748
+ taskId: input.task_id,
19749
+ engineOverride: input.engine_override,
19750
+ timeoutMs: input.timeout_ms,
19751
+ skipCache: input.skip_cache
19752
+ });
19753
+ try {
19754
+ const taskEngine = getTaskEngine();
19755
+ const session = deps.getSession();
19756
+ const options = {
19757
+ engineOverride: input.engine_override,
19758
+ timeoutMs: input.timeout_ms,
19759
+ skipCache: input.skip_cache
19760
+ };
19761
+ const result = await taskEngine.runTask(input.task_id, options);
19762
+ const output = {
19763
+ task_id: result.taskId,
19764
+ status: result.status,
19765
+ result: result.result,
19766
+ engine: result.engine,
19767
+ metrics: {
19768
+ duration_ms: result.metrics?.durationMs ?? 0,
19769
+ tokens_prompt: result.metrics?.tokensPrompt ?? null,
19770
+ tokens_completion: result.metrics?.tokensCompletion ?? null
19771
+ },
19772
+ cache_hit: result.cacheHit
19773
+ };
19774
+ if (result.error) {
19775
+ output.error = {
19776
+ code: result.error.code,
19777
+ message: result.error.message
19778
+ };
19779
+ }
19780
+ logger.info("[run_task] Task execution completed", {
19781
+ taskId: result.taskId,
19782
+ status: result.status,
19783
+ engine: result.engine,
19784
+ cacheHit: result.cacheHit,
19785
+ durationMs: Date.now() - startTime
19786
+ });
19787
+ return output;
19788
+ } catch (error) {
19789
+ logger.error("[run_task] Failed to execute task", {
19790
+ error: error instanceof Error ? error.message : String(error),
19791
+ taskId: input.task_id
19792
+ });
19793
+ throw error;
19794
+ }
19795
+ };
19796
+ }
19797
+ var runTaskSchema = {
19798
+ name: "run_task",
19799
+ description: "Execute a previously created task and return results. Blocks until completion or timeout.",
19800
+ inputSchema: {
19801
+ type: "object",
19802
+ properties: {
19803
+ task_id: {
19804
+ type: "string",
19805
+ description: "Task ID to execute"
19806
+ },
19807
+ engine_override: {
19808
+ type: "string",
19809
+ enum: ["gemini", "claude", "codex", "ax-cli"],
19810
+ description: "Override the estimated engine"
19811
+ },
19812
+ timeout_ms: {
19813
+ type: "integer",
19814
+ minimum: 1e3,
19815
+ maximum: 3e5,
19816
+ default: 3e4,
19817
+ description: "Custom timeout in milliseconds"
19818
+ },
19819
+ skip_cache: {
19820
+ type: "boolean",
19821
+ default: false,
19822
+ description: "Skip cache and force re-execution"
19823
+ }
19824
+ },
19825
+ required: ["task_id"]
19826
+ }
19827
+ };
19828
+
19829
+ // src/mcp/tools/task/get-task-result.ts
19830
+ init_esm_shims();
19831
+ init_logger();
19832
+ function createGetTaskResultHandler() {
19833
+ return async (input) => {
19834
+ logger.info("[get_task_result] Retrieving task", {
19835
+ taskId: input.task_id,
19836
+ includePayload: input.include_payload
19837
+ });
19838
+ try {
19839
+ const taskEngine = getTaskEngine();
19840
+ const task = taskEngine.getTask(input.task_id);
19841
+ if (!task) {
19842
+ throw new Error(`Task not found: ${input.task_id}`);
19843
+ }
19844
+ const output = {
19845
+ task_id: task.id,
19846
+ status: task.status,
19847
+ type: task.type,
19848
+ result: task.result,
19849
+ engine: task.engine,
19850
+ created_at: new Date(task.createdAt).toISOString(),
19851
+ completed_at: task.completedAt ? new Date(task.completedAt).toISOString() : null,
19852
+ expires_at: new Date(task.expiresAt).toISOString()
19853
+ };
19854
+ if (input.include_payload) {
19855
+ output.payload = task.payload;
19856
+ }
19857
+ if (task.error) {
19858
+ output.error = {
19859
+ code: task.error.code,
19860
+ message: task.error.message
19861
+ };
19862
+ }
19863
+ logger.info("[get_task_result] Task retrieved", {
19864
+ taskId: task.id,
19865
+ status: task.status
19866
+ });
19867
+ return output;
19868
+ } catch (error) {
19869
+ logger.error("[get_task_result] Failed to retrieve task", {
19870
+ error: error instanceof Error ? error.message : String(error),
19871
+ taskId: input.task_id
19872
+ });
19873
+ throw error;
19874
+ }
19875
+ };
19876
+ }
19877
+ var getTaskResultSchema = {
19878
+ name: "get_task_result",
19879
+ description: "Retrieve the result of a task. Does not execute - use run_task for execution.",
19880
+ inputSchema: {
19881
+ type: "object",
19882
+ properties: {
19883
+ task_id: {
19884
+ type: "string",
19885
+ description: "Task ID to retrieve"
19886
+ },
19887
+ include_payload: {
19888
+ type: "boolean",
19889
+ default: false,
19890
+ description: "Include original payload in response"
19891
+ }
19892
+ },
19893
+ required: ["task_id"]
19894
+ }
19895
+ };
19896
+
19897
+ // src/mcp/tools/task/list-tasks.ts
19898
+ init_esm_shims();
19899
+ init_logger();
19900
+ function createListTasksHandler() {
19901
+ return async (input) => {
19902
+ const limit = Math.min(input.limit ?? 20, 100);
19903
+ const offset = input.offset ?? 0;
19904
+ logger.info("[list_tasks] Listing tasks", {
19905
+ status: input.status,
19906
+ type: input.type,
19907
+ engine: input.engine,
19908
+ limit,
19909
+ offset
19910
+ });
19911
+ try {
19912
+ const taskEngine = getTaskEngine();
19913
+ const filter = {
19914
+ status: input.status,
19915
+ type: input.type,
19916
+ engine: input.engine,
19917
+ limit,
19918
+ offset
19919
+ };
19920
+ const result = taskEngine.listTasks(filter);
19921
+ const tasks = result.tasks.map((task) => ({
19922
+ task_id: task.id,
19923
+ type: task.type,
19924
+ status: task.status,
19925
+ engine: task.engine,
19926
+ priority: task.priority,
19927
+ created_at: new Date(task.createdAt).toISOString(),
19928
+ expires_at: new Date(task.expiresAt).toISOString(),
19929
+ has_result: task.result !== null
19930
+ }));
19931
+ const output = {
19932
+ tasks,
19933
+ total: result.total,
19934
+ offset,
19935
+ limit,
19936
+ has_more: offset + tasks.length < result.total
19937
+ };
19938
+ logger.info("[list_tasks] Tasks listed", {
19939
+ returned: tasks.length,
19940
+ total: result.total,
19941
+ hasMore: output.has_more
19942
+ });
19943
+ return output;
19944
+ } catch (error) {
19945
+ logger.error("[list_tasks] Failed to list tasks", {
19946
+ error: error instanceof Error ? error.message : String(error)
19947
+ });
19948
+ throw error;
19949
+ }
19950
+ };
19951
+ }
19952
+ var listTasksSchema = {
19953
+ name: "list_tasks",
19954
+ description: "List tasks with optional filtering. Supports pagination.",
19955
+ inputSchema: {
19956
+ type: "object",
19957
+ properties: {
19958
+ status: {
19959
+ type: "string",
19960
+ enum: ["pending", "running", "completed", "failed", "expired"],
19961
+ description: "Filter by task status"
19962
+ },
19963
+ type: {
19964
+ type: "string",
19965
+ enum: ["web_search", "code_review", "code_generation", "analysis", "custom"],
19966
+ description: "Filter by task type"
19967
+ },
19968
+ engine: {
19969
+ type: "string",
19970
+ enum: ["gemini", "claude", "codex", "ax-cli"],
19971
+ description: "Filter by engine"
19972
+ },
19973
+ limit: {
19974
+ type: "integer",
19975
+ minimum: 1,
19976
+ maximum: 100,
19977
+ default: 20,
19978
+ description: "Maximum results to return"
19979
+ },
19980
+ offset: {
19981
+ type: "integer",
19982
+ minimum: 0,
19983
+ default: 0,
19984
+ description: "Offset for pagination"
19985
+ }
19986
+ },
19987
+ required: []
19988
+ }
19989
+ };
19990
+
19991
+ // src/mcp/tools/task/delete-task.ts
19992
+ init_esm_shims();
19993
+ init_logger();
19994
+ function createDeleteTaskHandler() {
19995
+ return async (input) => {
19996
+ logger.info("[delete_task] Deleting task", {
19997
+ taskId: input.task_id,
19998
+ force: input.force
19999
+ });
20000
+ try {
20001
+ const taskEngine = getTaskEngine();
20002
+ const task = taskEngine.getTask(input.task_id);
20003
+ if (!task) {
20004
+ return {
20005
+ task_id: input.task_id,
20006
+ deleted: false,
20007
+ previous_status: "unknown",
20008
+ message: `Task not found: ${input.task_id}`
20009
+ };
20010
+ }
20011
+ if (task.status === "running" && !input.force) {
20012
+ return {
20013
+ task_id: input.task_id,
20014
+ deleted: false,
20015
+ previous_status: task.status,
20016
+ message: "Cannot delete running task. Use force=true to override."
20017
+ };
20018
+ }
20019
+ const deleted = taskEngine.deleteTask(input.task_id);
20020
+ const output = {
20021
+ task_id: input.task_id,
20022
+ deleted,
20023
+ previous_status: task.status,
20024
+ message: deleted ? `Task ${input.task_id} deleted successfully` : `Failed to delete task ${input.task_id}`
20025
+ };
20026
+ logger.info("[delete_task] Task deletion result", {
20027
+ taskId: input.task_id,
20028
+ deleted,
20029
+ previousStatus: task.status
20030
+ });
20031
+ return output;
20032
+ } catch (error) {
20033
+ logger.error("[delete_task] Failed to delete task", {
20034
+ error: error instanceof Error ? error.message : String(error),
20035
+ taskId: input.task_id
20036
+ });
20037
+ throw error;
20038
+ }
20039
+ };
20040
+ }
20041
+ var deleteTaskSchema = {
20042
+ name: "delete_task",
20043
+ description: "Delete a task and its associated data. Cannot delete running tasks unless force=true.",
20044
+ inputSchema: {
20045
+ type: "object",
20046
+ properties: {
20047
+ task_id: {
20048
+ type: "string",
20049
+ description: "Task ID to delete"
20050
+ },
20051
+ force: {
20052
+ type: "boolean",
20053
+ default: false,
20054
+ description: "Force delete even if task is running"
20055
+ }
20056
+ },
20057
+ required: ["task_id"]
20058
+ }
20059
+ };
20060
+
18136
20061
  // src/providers/mcp/pool-manager.ts
18137
20062
  init_esm_shims();
18138
20063
  init_logger();
@@ -18552,7 +20477,7 @@ var ConnectionTimeoutError = class extends McpClientError {
18552
20477
  };
18553
20478
 
18554
20479
  // src/providers/mcp/pool-manager.ts
18555
- var DEFAULT_CONFIG3 = {
20480
+ var DEFAULT_CONFIG6 = {
18556
20481
  maxConnectionsPerProvider: 2,
18557
20482
  idleTimeoutMs: 3e5,
18558
20483
  // 5 minutes
@@ -18573,7 +20498,7 @@ var McpClientPool = class extends EventEmitter {
18573
20498
  drainHandler;
18574
20499
  constructor(config = {}) {
18575
20500
  super();
18576
- this.config = { ...DEFAULT_CONFIG3, ...config };
20501
+ this.config = { ...DEFAULT_CONFIG6, ...config };
18577
20502
  this.startHealthChecks();
18578
20503
  this.startIdleCleanup();
18579
20504
  this.drainHandler = () => this.drain();
@@ -20495,7 +22420,13 @@ var McpServer = class _McpServer {
20495
22420
  },
20496
22421
  required: ["agent", "task"]
20497
22422
  }
20498
- }
22423
+ },
22424
+ // v11.3.5: Task Engine tools
22425
+ createTaskSchema,
22426
+ runTaskSchema,
22427
+ getTaskResultSchema,
22428
+ listTasksSchema,
22429
+ deleteTaskSchema
20499
22430
  ];
20500
22431
  }
20501
22432
  /**
@@ -20670,6 +22601,15 @@ var McpServer = class _McpServer {
20670
22601
  profileLoader: this.profileLoader,
20671
22602
  memoryManager: this.memoryManager
20672
22603
  }));
22604
+ register("create_task", createCreateTaskHandler({
22605
+ getSession: () => this.session
22606
+ }));
22607
+ register("run_task", createRunTaskHandler({
22608
+ getSession: () => this.session
22609
+ }));
22610
+ register("get_task_result", createGetTaskResultHandler());
22611
+ register("list_tasks", createListTasksHandler());
22612
+ register("delete_task", createDeleteTaskHandler());
20673
22613
  logger.info("[MCP Server] Registered tools", {
20674
22614
  count: this.tools.size,
20675
22615
  tools: Array.from(this.tools.keys())