@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/index.js CHANGED
@@ -2,15 +2,15 @@
2
2
  import * as path4 from 'path';
3
3
  import path4__default, { dirname, join, isAbsolute, basename, resolve, extname as extname$1, relative, sep, normalize, parse as parse$1, delimiter } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
- import * as fs5 from 'fs/promises';
5
+ import * as fs6 from 'fs/promises';
6
6
  import { mkdir, appendFile, access as access$1, readFile, stat, rm, readdir, copyFile, writeFile, rename, unlink, constants as constants$1, realpath as realpath$1 } from 'fs/promises';
7
- import * as fs3 from 'fs';
7
+ import * as fs4 from 'fs';
8
8
  import { access, realpath, existsSync, readFileSync, constants, writeFileSync, mkdirSync, promises, statSync, readdirSync, createWriteStream, unlinkSync } from 'fs';
9
9
  import Database2 from 'better-sqlite3';
10
10
  import os2, { homedir, cpus } from 'os';
11
11
  import { findUp } from 'find-up';
12
12
  import { glob } from 'glob';
13
- import { exec, spawn, execSync, spawnSync } from 'child_process';
13
+ import { exec, spawn, execSync, execFile, spawnSync } from 'child_process';
14
14
  import { z, ZodError } from 'zod';
15
15
  import chalk5 from 'chalk';
16
16
  import ora8 from 'ora';
@@ -18,7 +18,7 @@ import * as readline2 from 'readline';
18
18
  import readline2__default, { createInterface } from 'readline';
19
19
  import { Mutex } from 'async-mutex';
20
20
  import { promisify } from 'util';
21
- import crypto2, { randomUUID, createHash } from 'crypto';
21
+ import crypto4, { randomUUID, createHash, randomBytes } from 'crypto';
22
22
  import yargs from 'yargs';
23
23
  import { hideBin } from 'yargs/helpers';
24
24
  import * as yaml4 from 'js-yaml';
@@ -30,6 +30,7 @@ import addFormats from 'ajv-formats';
30
30
  import { EventEmitter } from 'events';
31
31
  import * as sqliteVec from 'sqlite-vec';
32
32
  import yaml from 'yaml';
33
+ import { gzipSync, gunzipSync } from 'zlib';
33
34
  import { parse } from '@iarna/toml';
34
35
 
35
36
  var __defProp = Object.defineProperty;
@@ -743,16 +744,16 @@ var init_errors = __esm({
743
744
  constructor(message, code = "E1001" /* CONFIG_INVALID */, suggestions = [], context) {
744
745
  super(message, code, suggestions, context);
745
746
  }
746
- static notFound(path8) {
747
+ static notFound(path9) {
747
748
  return new _ConfigError(
748
- `Configuration file not found: ${path8}`,
749
+ `Configuration file not found: ${path9}`,
749
750
  "E1000" /* CONFIG_NOT_FOUND */,
750
751
  [
751
752
  'Run "automatosx setup" to create a new configuration',
752
753
  "Specify a custom config path with --config option",
753
754
  "Check that you are in a valid AutomatosX project directory"
754
755
  ],
755
- { path: path8 }
756
+ { path: path9 }
756
757
  );
757
758
  }
758
759
  static invalid(reason, context) {
@@ -767,7 +768,7 @@ var init_errors = __esm({
767
768
  context
768
769
  );
769
770
  }
770
- static parseError(error, path8) {
771
+ static parseError(error, path9) {
771
772
  return new _ConfigError(
772
773
  `Failed to parse configuration: ${error.message}`,
773
774
  "E1002" /* CONFIG_PARSE_ERROR */,
@@ -776,7 +777,7 @@ var init_errors = __esm({
776
777
  "Use a JSON validator to find syntax errors",
777
778
  'Reset to default with "automatosx setup --force"'
778
779
  ],
779
- { path: path8, originalError: error.message }
780
+ { path: path9, originalError: error.message }
780
781
  );
781
782
  }
782
783
  };
@@ -1004,8 +1005,8 @@ __export(path_resolver_exports, {
1004
1005
  PathResolver: () => PathResolver,
1005
1006
  detectProjectRoot: () => detectProjectRoot
1006
1007
  });
1007
- function isWindowsPath(path8) {
1008
- return /^[a-zA-Z]:[/\\]/.test(path8);
1008
+ function isWindowsPath(path9) {
1009
+ return /^[a-zA-Z]:[/\\]/.test(path9);
1009
1010
  }
1010
1011
  async function detectProjectRoot(startDir = process.cwd()) {
1011
1012
  if (process.env.AUTOMATOSX_PROJECT_ROOT) {
@@ -1111,8 +1112,8 @@ var init_path_resolver = __esm({
1111
1112
  /**
1112
1113
  * Validate path is within allowed base directory
1113
1114
  */
1114
- validatePath(path8, baseDir) {
1115
- const normalized = normalizePath(resolvePath(path8));
1115
+ validatePath(path9, baseDir) {
1116
+ const normalized = normalizePath(resolvePath(path9));
1116
1117
  const base = normalizePath(resolvePath(baseDir));
1117
1118
  const separator = "/";
1118
1119
  const pathWithSep = normalized + separator;
@@ -1122,15 +1123,15 @@ var init_path_resolver = __esm({
1122
1123
  /**
1123
1124
  * Check if path is within allowed boundaries
1124
1125
  */
1125
- isPathAllowed(path8) {
1126
- const boundary = this.checkBoundaries(path8);
1126
+ isPathAllowed(path9) {
1127
+ const boundary = this.checkBoundaries(path9);
1127
1128
  return boundary === "agent_workspace" || boundary === "user_project";
1128
1129
  }
1129
1130
  /**
1130
1131
  * Check which boundary a path belongs to
1131
1132
  */
1132
- checkBoundaries(path8) {
1133
- const normalized = resolvePath(path8);
1133
+ checkBoundaries(path9) {
1134
+ const normalized = resolvePath(path9);
1134
1135
  if (this.validatePath(normalized, this.config.agentWorkspace)) {
1135
1136
  return "agent_workspace";
1136
1137
  }
@@ -1148,15 +1149,15 @@ var init_path_resolver = __esm({
1148
1149
  /**
1149
1150
  * Get relative path from project root
1150
1151
  */
1151
- getRelativeToProject(path8) {
1152
- const normalized = resolvePath(path8);
1152
+ getRelativeToProject(path9) {
1153
+ const normalized = resolvePath(path9);
1153
1154
  return normalizePath(getRelativePath(this.config.projectDir, normalized));
1154
1155
  }
1155
1156
  /**
1156
1157
  * Get relative path from working directory
1157
1158
  */
1158
- getRelativeToWorking(path8) {
1159
- const normalized = resolvePath(path8);
1159
+ getRelativeToWorking(path9) {
1160
+ const normalized = resolvePath(path9);
1160
1161
  return normalizePath(getRelativePath(this.config.workingDir, normalized));
1161
1162
  }
1162
1163
  /**
@@ -1175,11 +1176,11 @@ var init_path_resolver = __esm({
1175
1176
  * Validate path is within project boundaries
1176
1177
  * @throws PathError if outside project
1177
1178
  */
1178
- validateInProject(path8) {
1179
- const boundary = this.checkBoundaries(path8);
1179
+ validateInProject(path9) {
1180
+ const boundary = this.checkBoundaries(path9);
1180
1181
  if (boundary === "outside_boundaries" || boundary === "system_restricted") {
1181
1182
  throw new PathError("Path outside project directory", {
1182
- path: path8,
1183
+ path: path9,
1183
1184
  projectDir: this.config.projectDir,
1184
1185
  boundary
1185
1186
  });
@@ -1422,11 +1423,11 @@ var init_workspace_indexer = __esm({
1422
1423
  /**
1423
1424
  * Detect file type and language
1424
1425
  */
1425
- detectTypeAndLanguage(path8, ext) {
1426
- if (path8.match(/package\.json|tsconfig\.json|\.config\.(js|ts|json|yaml|yml)|\.eslintrc|\.prettierrc/)) {
1426
+ detectTypeAndLanguage(path9, ext) {
1427
+ if (path9.match(/package\.json|tsconfig\.json|\.config\.(js|ts|json|yaml|yml)|\.eslintrc|\.prettierrc/)) {
1427
1428
  return { type: "config", language: "json" };
1428
1429
  }
1429
- if (path8.match(/\.(test|spec)\.(ts|js|tsx|jsx)$/) || path8.includes("__tests__")) {
1430
+ if (path9.match(/\.(test|spec)\.(ts|js|tsx|jsx)$/) || path9.includes("__tests__")) {
1430
1431
  const lang = LANGUAGE_MAP[ext];
1431
1432
  return { type: "test", language: lang };
1432
1433
  }
@@ -1443,15 +1444,15 @@ var init_workspace_indexer = __esm({
1443
1444
  *
1444
1445
  * Higher score = more likely to be relevant
1445
1446
  */
1446
- calculateImportance(path8, type, size) {
1447
+ calculateImportance(path9, type, size) {
1447
1448
  let score = 0.5;
1448
- if (path8.match(/^(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 1;
1449
- if (path8.match(/^src\/(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 0.95;
1450
- if (path8.match(/^src\/cli\/(index|main)\.(ts|js)$/)) score = 0.9;
1449
+ if (path9.match(/^(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 1;
1450
+ if (path9.match(/^src\/(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 0.95;
1451
+ if (path9.match(/^src\/cli\/(index|main)\.(ts|js)$/)) score = 0.9;
1451
1452
  if (type === "config") score = 0.85;
1452
- if (path8.includes("/core/")) score += 0.15;
1453
- if (path8.includes("/types/")) score += 0.1;
1454
- if (path8.includes("/utils/")) score += 0.05;
1453
+ if (path9.includes("/core/")) score += 0.15;
1454
+ if (path9.includes("/types/")) score += 0.1;
1455
+ if (path9.includes("/utils/")) score += 0.05;
1455
1456
  if (type === "test") score = Math.max(0.3, score - 0.3);
1456
1457
  if (type === "doc") score = Math.max(0.4, score - 0.2);
1457
1458
  if (size > 1e4) score += 0.05;
@@ -1927,10 +1928,10 @@ function findOnPathUnix(cmdBase) {
1927
1928
  try {
1928
1929
  const which = spawnSync("which", [cmdBase], { timeout: 3e3 });
1929
1930
  if (which.status === 0) {
1930
- const path8 = which.stdout.toString().trim();
1931
- if (path8) {
1932
- logger.debug("Found via which", { cmdBase, path: path8 });
1933
- return { found: true, path: path8 };
1931
+ const path9 = which.stdout.toString().trim();
1932
+ if (path9) {
1933
+ logger.debug("Found via which", { cmdBase, path: path9 });
1934
+ return { found: true, path: path9 };
1934
1935
  }
1935
1936
  }
1936
1937
  } catch (error) {
@@ -3058,7 +3059,7 @@ ${request.prompt}`;
3058
3059
  }
3059
3060
  if (process.env.AUTOMATOSX_DEBUG_PROMPT === "true") {
3060
3061
  const debugPath = path4.join(process.cwd(), "automatosx/tmp/debug-prompt.txt");
3061
- fs3.writeFileSync(debugPath, `=== FULL PROMPT SENT TO ${this.getCLICommand()} ===
3062
+ fs4.writeFileSync(debugPath, `=== FULL PROMPT SENT TO ${this.getCLICommand()} ===
3062
3063
 
3063
3064
  ${fullPrompt}
3064
3065
 
@@ -4754,13 +4755,14 @@ var init_response_parser = __esm({
4754
4755
  };
4755
4756
  }
4756
4757
  });
4757
- var execAsync3, AxCliAdapter;
4758
+ var execFileAsync, execAsync3, AxCliAdapter;
4758
4759
  var init_adapter = __esm({
4759
4760
  "src/integrations/ax-cli/adapter.ts"() {
4760
4761
  init_esm_shims();
4761
4762
  init_command_builder();
4762
4763
  init_response_parser();
4763
4764
  init_logger();
4765
+ execFileAsync = promisify(execFile);
4764
4766
  execAsync3 = promisify(exec);
4765
4767
  AxCliAdapter = class {
4766
4768
  command = "ax-cli";
@@ -4792,7 +4794,7 @@ var init_adapter = __esm({
4792
4794
  model: options.model
4793
4795
  });
4794
4796
  try {
4795
- const { stdout, stderr } = await execAsync3(`${command} ${args.join(" ")}`, {
4797
+ const { stdout, stderr } = await execFileAsync(command, args, {
4796
4798
  timeout,
4797
4799
  maxBuffer: 10 * 1024 * 1024,
4798
4800
  // 10MB
@@ -4818,14 +4820,29 @@ var init_adapter = __esm({
4818
4820
  latencyMs
4819
4821
  };
4820
4822
  } catch (error) {
4823
+ const latencyMs = Date.now() - startTime;
4824
+ const execError = error;
4825
+ const stderr = execError.stderr || "";
4826
+ const stdout = execError.stdout || "";
4827
+ const exitCode = execError.code;
4821
4828
  logger.error("ax-cli execution failed", {
4822
4829
  error: error instanceof Error ? error.message : String(error),
4823
- command: fullCommand
4830
+ command: fullCommand,
4831
+ exitCode,
4832
+ stderr: stderr.substring(0, 500),
4833
+ latencyMs
4824
4834
  });
4825
- if (error instanceof Error) {
4826
- throw new Error(`ax-cli execution failed: ${error.message}`);
4835
+ let errorMessage = error instanceof Error ? error.message : String(error);
4836
+ const extractedError = this.responseParser.extractError(stderr || stdout);
4837
+ if (extractedError) {
4838
+ errorMessage = extractedError;
4827
4839
  }
4828
- throw error;
4840
+ const wrappedError = new Error(`ax-cli execution failed: ${errorMessage}`);
4841
+ wrappedError.exitCode = exitCode;
4842
+ wrappedError.stderr = stderr;
4843
+ wrappedError.stdout = stdout;
4844
+ wrappedError.latencyMs = latencyMs;
4845
+ throw wrappedError;
4829
4846
  }
4830
4847
  }
4831
4848
  /**
@@ -5224,14 +5241,18 @@ ${task.task}` : task.task;
5224
5241
  let totalTokens = 0;
5225
5242
  const timeoutMs = this.options.timeout;
5226
5243
  let timeoutTimer = null;
5244
+ let timedOut = false;
5245
+ let streamPromise = null;
5227
5246
  try {
5228
5247
  const timeoutPromise = new Promise((_, reject) => {
5229
5248
  timeoutTimer = setTimeout(() => {
5249
+ timedOut = true;
5230
5250
  reject(new Error(`Subagent task timed out after ${timeoutMs}ms`));
5231
5251
  }, timeoutMs);
5232
5252
  });
5233
- const streamPromise = (async () => {
5253
+ streamPromise = (async () => {
5234
5254
  for await (const chunk of subagent.processUserMessageStream(prompt)) {
5255
+ if (timedOut) break;
5235
5256
  if (chunk.type === "content") {
5236
5257
  content += chunk.content || "";
5237
5258
  }
@@ -5246,6 +5267,26 @@ ${task.task}` : task.task;
5246
5267
  clearTimeout(timeoutTimer);
5247
5268
  timeoutTimer = null;
5248
5269
  }
5270
+ if (timedOut) {
5271
+ try {
5272
+ if (typeof subagent.dispose === "function") {
5273
+ const disposeResult = subagent.dispose();
5274
+ if (disposeResult instanceof Promise) {
5275
+ await disposeResult.catch(() => {
5276
+ });
5277
+ }
5278
+ }
5279
+ if (streamPromise) {
5280
+ await Promise.race([
5281
+ streamPromise.catch(() => {
5282
+ }),
5283
+ new Promise((resolve13) => setTimeout(resolve13, 1e3))
5284
+ // 1s max wait
5285
+ ]);
5286
+ }
5287
+ } catch {
5288
+ }
5289
+ }
5249
5290
  }
5250
5291
  const latencyMs = Date.now() - startTime;
5251
5292
  this.emitEvent("task_complete", task.task, content);
@@ -5709,8 +5750,8 @@ var init_checkpoint_adapter = __esm({
5709
5750
  */
5710
5751
  async list(workflowId) {
5711
5752
  try {
5712
- await fs5.mkdir(this.checkpointDir, { recursive: true });
5713
- const files = await fs5.readdir(this.checkpointDir);
5753
+ await fs6.mkdir(this.checkpointDir, { recursive: true });
5754
+ const files = await fs6.readdir(this.checkpointDir);
5714
5755
  const prefix = `${workflowId}-`;
5715
5756
  const matchingFiles = files.filter((file) => {
5716
5757
  if (!file.startsWith(prefix) || !file.endsWith(".json")) {
@@ -5722,7 +5763,7 @@ var init_checkpoint_adapter = __esm({
5722
5763
  const results = await Promise.all(
5723
5764
  matchingFiles.map(async (file) => {
5724
5765
  try {
5725
- const content = await fs5.readFile(
5766
+ const content = await fs6.readFile(
5726
5767
  path4.join(this.checkpointDir, file),
5727
5768
  "utf-8"
5728
5769
  );
@@ -5830,7 +5871,7 @@ ${result.content.substring(0, 1e3)}
5830
5871
  await this.sdkCheckpointManager.delete(workflowId);
5831
5872
  }
5832
5873
  async saveToFileSystem(checkpoint) {
5833
- await fs5.mkdir(this.checkpointDir, { recursive: true });
5874
+ await fs6.mkdir(this.checkpointDir, { recursive: true });
5834
5875
  const filename = `${checkpoint.workflowId}-${checkpoint.phase}.json`;
5835
5876
  const filepath = path4.join(this.checkpointDir, filename);
5836
5877
  const content = JSON.stringify(checkpoint, null, 2);
@@ -5850,7 +5891,7 @@ ${result.content.substring(0, 1e3)}
5850
5891
  */
5851
5892
  async deleteFromFileSystem(workflowId) {
5852
5893
  try {
5853
- const files = await fs5.readdir(this.checkpointDir);
5894
+ const files = await fs6.readdir(this.checkpointDir);
5854
5895
  const prefix = `${workflowId}-`;
5855
5896
  for (const file of files) {
5856
5897
  if (!file.startsWith(prefix) || !file.endsWith(".json")) {
@@ -5858,7 +5899,7 @@ ${result.content.substring(0, 1e3)}
5858
5899
  }
5859
5900
  const middle = file.slice(prefix.length, -5);
5860
5901
  if (/^\d+$/.test(middle)) {
5861
- await fs5.unlink(path4.join(this.checkpointDir, file));
5902
+ await fs6.unlink(path4.join(this.checkpointDir, file));
5862
5903
  }
5863
5904
  }
5864
5905
  } catch (error) {
@@ -5876,7 +5917,7 @@ ${result.content.substring(0, 1e3)}
5876
5917
  for (const checkpoint of toDelete) {
5877
5918
  try {
5878
5919
  const filename = `${checkpoint.workflowId}-${checkpoint.phase}.json`;
5879
- await fs5.unlink(path4.join(this.checkpointDir, filename));
5920
+ await fs6.unlink(path4.join(this.checkpointDir, filename));
5880
5921
  } catch {
5881
5922
  }
5882
5923
  }
@@ -5920,6 +5961,9 @@ ${result.content.substring(0, 1e3)}
5920
5961
  }
5921
5962
  }
5922
5963
  }, this.options.autoSaveInterval);
5964
+ if (this.autoSaveTimer && typeof this.autoSaveTimer.unref === "function") {
5965
+ this.autoSaveTimer.unref();
5966
+ }
5923
5967
  }
5924
5968
  /**
5925
5969
  * Mark checkpoint for auto-save
@@ -6002,12 +6046,12 @@ ${result.content.substring(0, 1e3)}
6002
6046
  */
6003
6047
  async atomicWrite(filepath, content) {
6004
6048
  const tempFile = `${filepath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
6005
- await fs5.writeFile(tempFile, content, "utf-8");
6049
+ await fs6.writeFile(tempFile, content, "utf-8");
6006
6050
  try {
6007
- await fs5.rename(tempFile, filepath);
6051
+ await fs6.rename(tempFile, filepath);
6008
6052
  } catch (renameError) {
6009
6053
  try {
6010
- await fs5.unlink(tempFile);
6054
+ await fs6.unlink(tempFile);
6011
6055
  } catch {
6012
6056
  }
6013
6057
  throw renameError;
@@ -6139,7 +6183,7 @@ var init_instructions_bridge = __esm({
6139
6183
  async loadAgentProfile(agentName) {
6140
6184
  const profilePath = path4.join(this.options.agentsDir, `${agentName}.ax.yaml`);
6141
6185
  try {
6142
- const content = await fs5.readFile(profilePath, "utf-8");
6186
+ const content = await fs6.readFile(profilePath, "utf-8");
6143
6187
  const profile = this.parseYamlProfile(content);
6144
6188
  logger.debug("Agent profile loaded", { agentName, profilePath });
6145
6189
  return profile;
@@ -6174,7 +6218,7 @@ var init_instructions_bridge = __esm({
6174
6218
  }
6175
6219
  }
6176
6220
  try {
6177
- const content = await fs5.readFile(this.options.axCliCustomPath, "utf-8");
6221
+ const content = await fs6.readFile(this.options.axCliCustomPath, "utf-8");
6178
6222
  logger.debug("Custom instructions loaded from file", {
6179
6223
  path: this.options.axCliCustomPath
6180
6224
  });
@@ -6198,11 +6242,18 @@ var init_instructions_bridge = __esm({
6198
6242
  try {
6199
6243
  const sdk = await import('@defai.digital/ax-cli/sdk');
6200
6244
  if (sdk.buildSystemPrompt) {
6201
- const fullPrompt = sdk.buildSystemPrompt({
6245
+ const buildResult = sdk.buildSystemPrompt({
6202
6246
  customInstructions: "",
6203
6247
  includeMemory: true
6204
6248
  });
6205
- const contextMatch = fullPrompt?.match(/## Project Context\n\n([\s\S]*?)(?=\n##|$)/);
6249
+ const fullPrompt = buildResult instanceof Promise ? await buildResult : buildResult;
6250
+ if (typeof fullPrompt !== "string") {
6251
+ logger.debug("buildSystemPrompt did not return a string", {
6252
+ type: typeof fullPrompt
6253
+ });
6254
+ return "";
6255
+ }
6256
+ const contextMatch = fullPrompt.match(/## Project Context\n\n([\s\S]*?)(?=\n##|$)/);
6206
6257
  if (contextMatch && contextMatch[1]) {
6207
6258
  logger.debug("Project memory loaded via SDK");
6208
6259
  return contextMatch[1].trim();
@@ -6332,8 +6383,8 @@ ${profile.expertise.map((e) => `- ${e}`).join("\n")}
6332
6383
  ${profile.instructions}
6333
6384
  `;
6334
6385
  try {
6335
- await fs5.mkdir(path4.dirname(this.options.axCliCustomPath), { recursive: true });
6336
- await fs5.writeFile(this.options.axCliCustomPath, customContent, "utf-8");
6386
+ await fs6.mkdir(path4.dirname(this.options.axCliCustomPath), { recursive: true });
6387
+ await fs6.writeFile(this.options.axCliCustomPath, customContent, "utf-8");
6337
6388
  logger.info("Agent synced to ax-cli custom instructions", {
6338
6389
  agentName,
6339
6390
  path: this.options.axCliCustomPath
@@ -6389,7 +6440,7 @@ ${profile.instructions}
6389
6440
  }
6390
6441
  async getFileSignature(filePath) {
6391
6442
  try {
6392
- const stats = await fs5.stat(filePath);
6443
+ const stats = await fs6.stat(filePath);
6393
6444
  return `${stats.mtimeMs}:${stats.size}`;
6394
6445
  } catch {
6395
6446
  return "missing";
@@ -6582,6 +6633,12 @@ var init_mcp_manager = __esm({
6582
6633
  const { MCPManager } = this.mcpModule;
6583
6634
  if (MCPManager?.loadMCPConfig) {
6584
6635
  const config = await MCPManager.loadMCPConfig();
6636
+ if (config === null || config === void 0) {
6637
+ return { ok: false, error: new Error("loadMCPConfig returned null/undefined") };
6638
+ }
6639
+ if (typeof config !== "object" || Array.isArray(config)) {
6640
+ return { ok: false, error: new Error(`loadMCPConfig returned invalid type: ${typeof config}`) };
6641
+ }
6585
6642
  return { ok: true, value: config };
6586
6643
  }
6587
6644
  return { ok: false, error: new Error("loadMCPConfig not available in SDK") };
@@ -7297,7 +7354,7 @@ var init_adapter2 = __esm({
7297
7354
  const { createAgent } = await import('@defai.digital/ax-cli/sdk');
7298
7355
  try {
7299
7356
  const config = {
7300
- maxToolRounds: options.maxToolRounds || DEFAULT_MAX_TOOL_ROUNDS
7357
+ maxToolRounds: options.maxToolRounds ?? DEFAULT_MAX_TOOL_ROUNDS
7301
7358
  };
7302
7359
  logger.debug("Creating SDK agent (credentials from ax-cli settings)", {
7303
7360
  maxToolRounds: config.maxToolRounds,
@@ -7359,11 +7416,11 @@ var init_adapter2 = __esm({
7359
7416
  */
7360
7417
  hasConfigChanged(options) {
7361
7418
  if (!this.agentConfig) return true;
7362
- const changed = this.agentConfig.maxToolRounds !== (options.maxToolRounds || DEFAULT_MAX_TOOL_ROUNDS);
7419
+ const changed = this.agentConfig.maxToolRounds !== (options.maxToolRounds ?? DEFAULT_MAX_TOOL_ROUNDS);
7363
7420
  if (changed) {
7364
7421
  logger.debug("Agent config changed, will reinitialize", {
7365
7422
  oldMaxToolRounds: this.agentConfig.maxToolRounds,
7366
- newMaxToolRounds: options.maxToolRounds || DEFAULT_MAX_TOOL_ROUNDS,
7423
+ newMaxToolRounds: options.maxToolRounds ?? DEFAULT_MAX_TOOL_ROUNDS,
7367
7424
  note: "SDK credentials managed by ax-cli setup"
7368
7425
  });
7369
7426
  }
@@ -7472,15 +7529,18 @@ var init_adapter2 = __esm({
7472
7529
  return tokens;
7473
7530
  }
7474
7531
  const usage = usageObject || {};
7532
+ const promptTokens = Number(usage.prompt_tokens ?? usage.prompt ?? usage.input ?? usage.promptTokens ?? 0) || 0;
7533
+ const completionTokens = Number(usage.completion_tokens ?? usage.completion ?? usage.output ?? usage.completionTokens ?? 0) || 0;
7534
+ const totalFromUsage = Number(usage.total_tokens ?? usage.total ?? usage.totalTokens ?? 0) || 0;
7475
7535
  const actualTokens = {
7476
- prompt: usage.prompt_tokens ?? usage.prompt ?? usage.input ?? usage.promptTokens ?? 0,
7477
- completion: usage.completion_tokens ?? usage.completion ?? usage.output ?? usage.completionTokens ?? 0,
7478
- total: usage.total_tokens ?? usage.total ?? usage.totalTokens ?? 0
7536
+ prompt: promptTokens,
7537
+ completion: completionTokens,
7538
+ total: totalFromUsage > 0 ? totalFromUsage : promptTokens + completionTokens
7479
7539
  };
7480
7540
  if (actualTokens.total > 0) {
7481
7541
  logger.info(`Using token counts from ${sourceName || "usage object"}`, {
7482
7542
  tokens: TokenEstimator.format(actualTokens),
7483
- accuracy: "100%",
7543
+ accuracy: totalFromUsage > 0 ? "100%" : "100% (derived from prompt+completion)",
7484
7544
  source: sourceName || "usage"
7485
7545
  });
7486
7546
  return actualTokens;
@@ -8094,8 +8154,11 @@ var init_ax_cli_provider = __esm({
8094
8154
  const options = {
8095
8155
  model: request.model || axCliConfig.model,
8096
8156
  // Optional model override
8097
- maxToolRounds: axCliConfig.maxToolRounds || DEFAULT_MAX_TOOL_ROUNDS2,
8157
+ maxToolRounds: axCliConfig.maxToolRounds ?? DEFAULT_MAX_TOOL_ROUNDS2,
8098
8158
  timeout: this.config.timeout,
8159
+ // BUG FIX: Force JSON output for CLI adapter to ensure proper parsing
8160
+ // Without this, the response parser expects JSONL but gets plain text
8161
+ json: true,
8099
8162
  // Note: apiKey and baseUrl are ignored by SDK adapter (v3.7.0+)
8100
8163
  // SDK loads credentials from ax-cli setup (~/.ax-cli/config.json)
8101
8164
  apiKey: axCliConfig.apiKey,
@@ -9349,11 +9412,11 @@ var VALIDATION_LIMITS = {
9349
9412
  MAX_PORT: 65535
9350
9413
  // Maximum port number
9351
9414
  };
9352
- function isValidRelativePath(path8) {
9353
- if (!path8 || typeof path8 !== "string") {
9415
+ function isValidRelativePath(path9) {
9416
+ if (!path9 || typeof path9 !== "string") {
9354
9417
  return false;
9355
9418
  }
9356
- const normalizedPath = path8.replace(/\\/g, "/");
9419
+ const normalizedPath = path9.replace(/\\/g, "/");
9357
9420
  if (normalizedPath.startsWith("/")) {
9358
9421
  return false;
9359
9422
  }
@@ -10070,7 +10133,7 @@ var safeNameSchema = z.string().min(1).max(VALIDATION_LIMITS.MAX_NAME_LENGTH).re
10070
10133
  "Name must be alphanumeric with dash/underscore only"
10071
10134
  ).describe("Safe identifier name");
10072
10135
  var relativePathSchema = z.string().min(1).refine(
10073
- (path8) => !path8.includes("..") && !path8.startsWith("/"),
10136
+ (path9) => !path9.includes("..") && !path9.startsWith("/"),
10074
10137
  "Path must be relative (no ../, no absolute paths)"
10075
10138
  ).describe("Relative path within project");
10076
10139
  var fileExtensionSchema = z.string().regex(
@@ -10410,16 +10473,16 @@ async function loadConfigUncached(projectDir) {
10410
10473
  });
10411
10474
  return config;
10412
10475
  }
10413
- async function loadConfigFile(path8) {
10476
+ async function loadConfigFile(path9) {
10414
10477
  try {
10415
- const content = await readFile(path8, "utf-8");
10478
+ const content = await readFile(path9, "utf-8");
10416
10479
  if (content.length > VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE) {
10417
10480
  throw ConfigError.parseError(
10418
10481
  new Error(`Config file too large (max ${VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE / 1024}KB, got ${Math.ceil(content.length / 1024)}KB)`),
10419
- path8
10482
+ path9
10420
10483
  );
10421
10484
  }
10422
- const ext = extname(path8).toLowerCase();
10485
+ const ext = extname(path9).toLowerCase();
10423
10486
  let userConfig;
10424
10487
  try {
10425
10488
  if (ext === ".yaml" || ext === ".yml") {
@@ -10428,7 +10491,7 @@ async function loadConfigFile(path8) {
10428
10491
  userConfig = JSON.parse(content);
10429
10492
  }
10430
10493
  } catch (parseError) {
10431
- throw ConfigError.parseError(parseError, path8);
10494
+ throw ConfigError.parseError(parseError, path9);
10432
10495
  }
10433
10496
  const config = mergeConfig(DEFAULT_CONFIG, userConfig);
10434
10497
  if (config.execution && userConfig.execution?.maxConcurrentAgents === void 0) {
@@ -10444,35 +10507,35 @@ async function loadConfigFile(path8) {
10444
10507
  if (validationErrors.length > 0) {
10445
10508
  throw ConfigError.invalid(
10446
10509
  validationErrors.join("; "),
10447
- { path: path8, errors: validationErrors }
10510
+ { path: path9, errors: validationErrors }
10448
10511
  );
10449
10512
  }
10450
- logger.info("Config loaded successfully", { path: normalizePath(path8), format: ext });
10513
+ logger.info("Config loaded successfully", { path: normalizePath(path9), format: ext });
10451
10514
  return config;
10452
10515
  } catch (error) {
10453
10516
  if (error instanceof ConfigError) {
10454
10517
  throw error;
10455
10518
  }
10456
10519
  if (error.code === "ENOENT") {
10457
- throw ConfigError.notFound(path8);
10520
+ throw ConfigError.notFound(path9);
10458
10521
  }
10459
10522
  if (error.code === "EACCES") {
10460
10523
  throw new ConfigError(
10461
- `Permission denied reading config: ${path8}`,
10524
+ `Permission denied reading config: ${path9}`,
10462
10525
  "E1002" /* CONFIG_PARSE_ERROR */,
10463
10526
  [
10464
10527
  "Check file permissions",
10465
10528
  "Run with appropriate user privileges",
10466
10529
  "Verify the file is accessible"
10467
10530
  ],
10468
- { path: path8, error: error.message }
10531
+ { path: path9, error: error.message }
10469
10532
  );
10470
10533
  }
10471
10534
  throw new ConfigError(
10472
10535
  `Failed to load config: ${error.message}`,
10473
10536
  "E1002" /* CONFIG_PARSE_ERROR */,
10474
10537
  ["Check file format and permissions"],
10475
- { path: path8, originalError: error.message }
10538
+ { path: path9, originalError: error.message }
10476
10539
  );
10477
10540
  }
10478
10541
  }
@@ -10488,8 +10551,8 @@ function validateConfigWithZod(config) {
10488
10551
  return ["Configuration validation failed with unknown error structure"];
10489
10552
  }
10490
10553
  return result.error.issues.map((err) => {
10491
- const path8 = err.path.join(".");
10492
- return `${path8}: ${err.message}`;
10554
+ const path9 = err.path.join(".");
10555
+ return `${path9}: ${err.message}`;
10493
10556
  });
10494
10557
  }
10495
10558
  function validateConfig(config) {
@@ -10875,16 +10938,16 @@ function validateConfig(config) {
10875
10938
  }
10876
10939
  return errors;
10877
10940
  }
10878
- async function saveConfigFile(path8, config) {
10941
+ async function saveConfigFile(path9, config) {
10879
10942
  try {
10880
10943
  const validationErrors = validateConfig(config);
10881
10944
  if (validationErrors.length > 0) {
10882
10945
  throw ConfigError.invalid(
10883
10946
  validationErrors.join("; "),
10884
- { path: path8, errors: validationErrors }
10947
+ { path: path9, errors: validationErrors }
10885
10948
  );
10886
10949
  }
10887
- const ext = extname(path8).toLowerCase();
10950
+ const ext = extname(path9).toLowerCase();
10888
10951
  let content;
10889
10952
  if (ext === ".yaml" || ext === ".yml") {
10890
10953
  content = dump(config, {
@@ -10896,30 +10959,30 @@ async function saveConfigFile(path8, config) {
10896
10959
  } else {
10897
10960
  content = JSON.stringify(config, null, 2);
10898
10961
  }
10899
- await writeFile(path8, content, "utf-8");
10962
+ await writeFile(path9, content, "utf-8");
10900
10963
  configCache.clear();
10901
- logger.info("Config saved successfully", { path: normalizePath(path8), format: ext });
10964
+ logger.info("Config saved successfully", { path: normalizePath(path9), format: ext });
10902
10965
  } catch (error) {
10903
10966
  if (error instanceof ConfigError) {
10904
10967
  throw error;
10905
10968
  }
10906
10969
  if (error.code === "EACCES") {
10907
10970
  throw new ConfigError(
10908
- `Permission denied writing config: ${path8}`,
10971
+ `Permission denied writing config: ${path9}`,
10909
10972
  "E1002" /* CONFIG_PARSE_ERROR */,
10910
10973
  [
10911
10974
  "Check file permissions",
10912
10975
  "Run with appropriate user privileges",
10913
10976
  "Verify the directory is writable"
10914
10977
  ],
10915
- { path: path8, error: error.message }
10978
+ { path: path9, error: error.message }
10916
10979
  );
10917
10980
  }
10918
10981
  throw new ConfigError(
10919
10982
  `Failed to save config: ${error.message}`,
10920
10983
  "E1002" /* CONFIG_PARSE_ERROR */,
10921
10984
  ["Check file path and permissions"],
10922
- { path: path8, originalError: error.message }
10985
+ { path: path9, originalError: error.message }
10923
10986
  );
10924
10987
  }
10925
10988
  }
@@ -11669,10 +11732,10 @@ var configCommand = {
11669
11732
  } else {
11670
11733
  const projectConfig = resolve(process.cwd(), "ax.config.json");
11671
11734
  const hiddenConfig = resolve(process.cwd(), ".automatosx", "config.json");
11672
- const fs8 = await import('fs');
11673
- if (fs8.existsSync(projectConfig)) {
11735
+ const fs9 = await import('fs');
11736
+ if (fs9.existsSync(projectConfig)) {
11674
11737
  configPath = projectConfig;
11675
- } else if (fs8.existsSync(hiddenConfig)) {
11738
+ } else if (fs9.existsSync(hiddenConfig)) {
11676
11739
  configPath = hiddenConfig;
11677
11740
  } else {
11678
11741
  configPath = projectConfig;
@@ -11719,9 +11782,9 @@ var configCommand = {
11719
11782
  }
11720
11783
  }
11721
11784
  };
11722
- async function checkExists(path8) {
11785
+ async function checkExists(path9) {
11723
11786
  try {
11724
- await access$1(path8, constants.F_OK);
11787
+ await access$1(path9, constants.F_OK);
11725
11788
  return true;
11726
11789
  } catch {
11727
11790
  return false;
@@ -11752,7 +11815,7 @@ async function validateConfigFile(config, verbose) {
11752
11815
  }
11753
11816
  console.log();
11754
11817
  }
11755
- async function resetConfig(path8, verbose) {
11818
+ async function resetConfig(path9, verbose) {
11756
11819
  const { createRequire } = await import('module');
11757
11820
  const require2 = createRequire(import.meta.url);
11758
11821
  let version = "11.2.6";
@@ -11767,14 +11830,14 @@ async function resetConfig(path8, verbose) {
11767
11830
  // Users should rely on IDE JSON Schema plugins that fetch from npm package
11768
11831
  version
11769
11832
  };
11770
- await saveConfigFile(path8, config);
11833
+ await saveConfigFile(path9, config);
11771
11834
  printSuccess("Configuration reset to defaults");
11772
11835
  if (verbose) {
11773
11836
  console.log(chalk5.gray(`
11774
- Config file: ${path8}
11837
+ Config file: ${path9}
11775
11838
  `));
11776
11839
  }
11777
- logger.info("Configuration reset", { path: path8 });
11840
+ logger.info("Configuration reset", { path: path9 });
11778
11841
  }
11779
11842
  async function listConfig(config, verbose) {
11780
11843
  console.log(chalk5.bold.cyan("\n\u{1F4CB} AutomatosX Configuration\n"));
@@ -11853,7 +11916,7 @@ async function getConfig(config, key, verbose) {
11853
11916
  }
11854
11917
  }
11855
11918
  }
11856
- async function setConfig(path8, config, key, value, verbose) {
11919
+ async function setConfig(path9, config, key, value, verbose) {
11857
11920
  let parsedValue = value;
11858
11921
  try {
11859
11922
  parsedValue = JSON.parse(value);
@@ -11872,21 +11935,21 @@ async function setConfig(path8, config, key, value, verbose) {
11872
11935
  console.log();
11873
11936
  process.exit(1);
11874
11937
  }
11875
- await saveConfigFile(path8, config);
11938
+ await saveConfigFile(path9, config);
11876
11939
  printSuccess(`Configuration updated: ${key} = ${value}`);
11877
11940
  if (verbose) {
11878
11941
  console.log(chalk5.gray(`
11879
- Config file: ${path8}`));
11942
+ Config file: ${path9}`));
11880
11943
  console.log(chalk5.gray("Configuration validated successfully\n"));
11881
11944
  }
11882
11945
  logger.info("Configuration updated", { key, value });
11883
11946
  }
11884
- function getNestedValue(obj, path8) {
11885
- if (!path8 || !path8.trim()) return void 0;
11886
- return path8.split(".").filter(Boolean).reduce((current, key) => current?.[key], obj);
11947
+ function getNestedValue(obj, path9) {
11948
+ if (!path9 || !path9.trim()) return void 0;
11949
+ return path9.split(".").filter(Boolean).reduce((current, key) => current?.[key], obj);
11887
11950
  }
11888
- function setNestedValue(obj, path8, value) {
11889
- const keys = path8.split(".");
11951
+ function setNestedValue(obj, path9, value) {
11952
+ const keys = path9.split(".");
11890
11953
  const lastKey = keys.pop();
11891
11954
  if (!lastKey) return false;
11892
11955
  const target = keys.reduce((current, key) => {
@@ -13986,7 +14049,7 @@ var setupCommand = {
13986
14049
  let version = "11.2.6";
13987
14050
  try {
13988
14051
  const packageJson = JSON.parse(
13989
- await import('fs/promises').then((fs8) => fs8.readFile(join(packageRoot, "package.json"), "utf-8"))
14052
+ await import('fs/promises').then((fs9) => fs9.readFile(join(packageRoot, "package.json"), "utf-8"))
13990
14053
  );
13991
14054
  version = packageJson.version;
13992
14055
  } catch {
@@ -14309,9 +14372,9 @@ var setupCommand = {
14309
14372
  }
14310
14373
  }
14311
14374
  };
14312
- async function checkExists2(path8) {
14375
+ async function checkExists2(path9) {
14313
14376
  try {
14314
- await access$1(path8, constants.F_OK);
14377
+ await access$1(path9, constants.F_OK);
14315
14378
  return true;
14316
14379
  } catch {
14317
14380
  return false;
@@ -15823,11 +15886,11 @@ async function detectExpressRoutes(projectDir) {
15823
15886
  while ((match = routeRegex.exec(content)) !== null) {
15824
15887
  if (match[1] && match[2]) {
15825
15888
  const method = match[1].toUpperCase();
15826
- const path8 = match[2];
15889
+ const path9 = match[2];
15827
15890
  endpoints.push({
15828
15891
  method,
15829
- path: path8,
15830
- group: path8.split("/")[1] || "api"
15892
+ path: path9,
15893
+ group: path9.split("/")[1] || "api"
15831
15894
  });
15832
15895
  }
15833
15896
  }
@@ -16932,12 +16995,12 @@ var listCommand = {
16932
16995
  };
16933
16996
  async function listAgents(pathResolver, format) {
16934
16997
  const agentsDir = pathResolver.getAgentsDirectory();
16935
- const { existsSync: existsSync27 } = await import('fs');
16998
+ const { existsSync: existsSync28 } = await import('fs');
16936
16999
  const projectDir = await detectProjectRoot();
16937
17000
  const examplesDir = join(projectDir, "examples", "agents");
16938
17001
  try {
16939
17002
  const agentFiles = [];
16940
- if (existsSync27(agentsDir)) {
17003
+ if (existsSync28(agentsDir)) {
16941
17004
  const files = await readdir(agentsDir);
16942
17005
  for (const file of files) {
16943
17006
  if (file.endsWith(".yaml") || file.endsWith(".yml")) {
@@ -16949,7 +17012,7 @@ async function listAgents(pathResolver, format) {
16949
17012
  }
16950
17013
  }
16951
17014
  }
16952
- if (existsSync27(examplesDir)) {
17015
+ if (existsSync28(examplesDir)) {
16953
17016
  const files = await readdir(examplesDir);
16954
17017
  for (const file of files) {
16955
17018
  if (file.endsWith(".yaml") || file.endsWith(".yml")) {
@@ -18821,18 +18884,18 @@ var ProviderSessionManager = class {
18821
18884
  };
18822
18885
  var providerSessionInstances = /* @__PURE__ */ new Map();
18823
18886
  async function getProviderSession(workspacePath) {
18824
- let path8;
18887
+ let path9;
18825
18888
  {
18826
18889
  if (process.env.NODE_ENV === "test" || process.env.VITEST) {
18827
- path8 = process.cwd();
18890
+ path9 = process.cwd();
18828
18891
  } else {
18829
- path8 = await detectProjectRoot();
18892
+ path9 = await detectProjectRoot();
18830
18893
  }
18831
18894
  }
18832
- if (!providerSessionInstances.has(path8)) {
18833
- providerSessionInstances.set(path8, new ProviderSessionManager(path8));
18895
+ if (!providerSessionInstances.has(path9)) {
18896
+ providerSessionInstances.set(path9, new ProviderSessionManager(path9));
18834
18897
  }
18835
- return providerSessionInstances.get(path8);
18898
+ return providerSessionInstances.get(path9);
18836
18899
  }
18837
18900
 
18838
18901
  // src/core/router/router.ts
@@ -24678,9 +24741,9 @@ var DependencyGraphBuilder = class {
24678
24741
  detectCycles(graph) {
24679
24742
  const visiting = /* @__PURE__ */ new Set();
24680
24743
  const visited = /* @__PURE__ */ new Set();
24681
- const visit = (nodeName, path8) => {
24744
+ const visit = (nodeName, path9) => {
24682
24745
  if (visiting.has(nodeName)) {
24683
- throw new Error(`Circular dependency detected: ${[...path8, nodeName].join(" \u2192 ")}`);
24746
+ throw new Error(`Circular dependency detected: ${[...path9, nodeName].join(" \u2192 ")}`);
24684
24747
  }
24685
24748
  if (visited.has(nodeName)) {
24686
24749
  return;
@@ -24695,7 +24758,7 @@ var DependencyGraphBuilder = class {
24695
24758
  }
24696
24759
  visiting.add(nodeName);
24697
24760
  for (const dependency of node.dependencies) {
24698
- visit(dependency, [...path8, nodeName]);
24761
+ visit(dependency, [...path9, nodeName]);
24699
24762
  }
24700
24763
  visiting.delete(nodeName);
24701
24764
  visited.add(nodeName);
@@ -26428,59 +26491,59 @@ var DANGEROUS_PATH_PATTERNS = [
26428
26491
  // Common data drive (Windows, alt format)
26429
26492
  ];
26430
26493
  var SUSPICIOUS_PATH_CHARS = /[<>:|"]/;
26431
- function validatePathParameter(path8, paramName, projectRoot = process.cwd()) {
26432
- if (!path8 || path8.trim() === "") {
26494
+ function validatePathParameter(path9, paramName, projectRoot = process.cwd()) {
26495
+ if (!path9 || path9.trim() === "") {
26433
26496
  throw new ValidationError(
26434
26497
  `Invalid ${paramName}: path cannot be empty`,
26435
26498
  -32602 /* InvalidParams */,
26436
- { path: path8, paramName }
26499
+ { path: path9, paramName }
26437
26500
  );
26438
26501
  }
26439
26502
  for (const pattern of DANGEROUS_PATH_PATTERNS) {
26440
- if (path8.includes(pattern)) {
26503
+ if (path9.includes(pattern)) {
26441
26504
  throw new ValidationError(
26442
26505
  `Invalid ${paramName}: path contains dangerous pattern "${pattern}"`,
26443
26506
  -32602 /* InvalidParams */,
26444
- { path: path8, paramName, pattern }
26507
+ { path: path9, paramName, pattern }
26445
26508
  );
26446
26509
  }
26447
26510
  }
26448
- if (isAbsolute(path8)) {
26511
+ if (isAbsolute(path9)) {
26449
26512
  throw new ValidationError(
26450
26513
  `Invalid ${paramName}: absolute paths are not allowed`,
26451
26514
  -32602 /* InvalidParams */,
26452
- { path: path8, paramName }
26515
+ { path: path9, paramName }
26453
26516
  );
26454
26517
  }
26455
26518
  try {
26456
- const resolvedPath = resolve(projectRoot, path8);
26519
+ const resolvedPath = resolve(projectRoot, path9);
26457
26520
  const normalizedRoot = resolve(projectRoot);
26458
26521
  if (!resolvedPath.startsWith(normalizedRoot + sep) && resolvedPath !== normalizedRoot) {
26459
26522
  throw new ValidationError(
26460
26523
  `Invalid ${paramName}: path escapes project boundary`,
26461
26524
  -32602 /* InvalidParams */,
26462
- { path: path8, paramName, projectRoot, resolvedPath }
26525
+ { path: path9, paramName, projectRoot, resolvedPath }
26463
26526
  );
26464
26527
  }
26465
26528
  } catch (error) {
26466
26529
  throw new ValidationError(
26467
26530
  `Invalid ${paramName}: path resolution failed`,
26468
26531
  -32602 /* InvalidParams */,
26469
- { path: path8, paramName, error: String(error) }
26532
+ { path: path9, paramName, error: String(error) }
26470
26533
  );
26471
26534
  }
26472
- if (path8.includes("\0")) {
26535
+ if (path9.includes("\0")) {
26473
26536
  throw new ValidationError(
26474
26537
  `Invalid ${paramName}: path contains null byte`,
26475
26538
  -32602 /* InvalidParams */,
26476
- { path: path8, paramName }
26539
+ { path: path9, paramName }
26477
26540
  );
26478
26541
  }
26479
- if (SUSPICIOUS_PATH_CHARS.test(path8)) {
26542
+ if (SUSPICIOUS_PATH_CHARS.test(path9)) {
26480
26543
  throw new ValidationError(
26481
26544
  `Invalid ${paramName}: path contains invalid characters`,
26482
26545
  -32602 /* InvalidParams */,
26483
- { path: path8, paramName }
26546
+ { path: path9, paramName }
26484
26547
  );
26485
26548
  }
26486
26549
  }
@@ -27176,8 +27239,8 @@ function createMemoryExportHandler(deps) {
27176
27239
  return async (input) => {
27177
27240
  logger.info("[MCP] memory_export called", { input });
27178
27241
  try {
27179
- const { path: path8 } = input;
27180
- const absolutePath = resolveExportPath(deps.pathResolver, path8);
27242
+ const { path: path9 } = input;
27243
+ const absolutePath = resolveExportPath(deps.pathResolver, path9);
27181
27244
  const exported = await deps.memoryManager.exportToJSON(absolutePath);
27182
27245
  const result = {
27183
27246
  success: true,
@@ -27213,8 +27276,8 @@ function createMemoryImportHandler(deps) {
27213
27276
  return async (input) => {
27214
27277
  logger.info("[MCP] memory_import called", { input });
27215
27278
  try {
27216
- const { path: path8 } = input;
27217
- const absolutePath = resolveImportPath(deps.pathResolver, path8);
27279
+ const { path: path9 } = input;
27280
+ const absolutePath = resolveImportPath(deps.pathResolver, path9);
27218
27281
  const imported = await deps.memoryManager.importFromJSON(absolutePath);
27219
27282
  const result = {
27220
27283
  success: true,
@@ -27569,6 +27632,1930 @@ Task: ${task}`;
27569
27632
  };
27570
27633
  }
27571
27634
 
27635
+ // src/mcp/tools/task/index.ts
27636
+ init_esm_shims();
27637
+
27638
+ // src/mcp/tools/task/create-task.ts
27639
+ init_esm_shims();
27640
+
27641
+ // src/core/task-engine/index.ts
27642
+ init_esm_shims();
27643
+
27644
+ // src/core/task-engine/types.ts
27645
+ init_esm_shims();
27646
+ var TaskEngineError = class _TaskEngineError extends Error {
27647
+ constructor(message, code, details) {
27648
+ super(message);
27649
+ this.code = code;
27650
+ this.details = details;
27651
+ this.name = "TaskEngineError";
27652
+ Error.captureStackTrace?.(this, _TaskEngineError);
27653
+ }
27654
+ };
27655
+ var LoopPreventionError = class extends TaskEngineError {
27656
+ constructor(message, callChain, code = "LOOP_DETECTED") {
27657
+ super(message, code, { callChain });
27658
+ this.callChain = callChain;
27659
+ this.name = "LoopPreventionError";
27660
+ }
27661
+ };
27662
+ var TaskTypeSchema = z.enum([
27663
+ "web_search",
27664
+ "code_review",
27665
+ "code_generation",
27666
+ "analysis",
27667
+ "custom"
27668
+ ]);
27669
+ var TaskEngineSchema = z.enum([
27670
+ "auto",
27671
+ "gemini",
27672
+ "claude",
27673
+ "codex",
27674
+ "ax-cli"
27675
+ ]);
27676
+ var TaskStatusSchema = z.enum([
27677
+ "pending",
27678
+ "running",
27679
+ "completed",
27680
+ "failed",
27681
+ "expired"
27682
+ ]);
27683
+ var OriginClientSchema = z.enum([
27684
+ "claude-code",
27685
+ "gemini-cli",
27686
+ "codex-cli",
27687
+ "ax-cli",
27688
+ "unknown"
27689
+ ]);
27690
+ var CreateTaskInputSchema = z.object({
27691
+ type: TaskTypeSchema,
27692
+ payload: z.record(z.string(), z.unknown()),
27693
+ engine: TaskEngineSchema.default("auto"),
27694
+ priority: z.number().int().min(1).max(10).default(5),
27695
+ ttlHours: z.number().int().min(1).default(24).transform((v) => Math.min(v, 168)),
27696
+ context: z.object({
27697
+ originClient: OriginClientSchema.optional(),
27698
+ callChain: z.array(z.string()).optional(),
27699
+ depth: z.number().int().min(0).optional()
27700
+ }).optional()
27701
+ });
27702
+ var TaskFilterSchema = z.object({
27703
+ status: TaskStatusSchema.optional(),
27704
+ engine: TaskEngineSchema.optional(),
27705
+ type: TaskTypeSchema.optional(),
27706
+ originClient: OriginClientSchema.optional(),
27707
+ limit: z.number().int().min(1).max(1e3).default(20),
27708
+ offset: z.number().int().min(0).default(0)
27709
+ });
27710
+ z.object({
27711
+ engineOverride: z.enum(["gemini", "claude", "codex", "ax-cli"]).optional(),
27712
+ timeoutMs: z.number().int().min(5e3).max(3e5).optional(),
27713
+ skipCache: z.boolean().default(false)
27714
+ });
27715
+ function isLoopPreventionError(error) {
27716
+ return error instanceof LoopPreventionError;
27717
+ }
27718
+
27719
+ // src/core/task-engine/loop-guard.ts
27720
+ init_esm_shims();
27721
+ var DEFAULT_BLOCKED_PATTERNS = [
27722
+ // Prevent recursive hub pattern
27723
+ /automatosx.*automatosx/
27724
+ ];
27725
+ var CLIENT_NORMALIZATION_MAP = {
27726
+ // Claude variants
27727
+ "claude": "claude-code",
27728
+ "claude-code": "claude-code",
27729
+ "claudecode": "claude-code",
27730
+ "anthropic": "claude-code",
27731
+ // Gemini variants
27732
+ "gemini": "gemini-cli",
27733
+ "gemini-cli": "gemini-cli",
27734
+ "geminicli": "gemini-cli",
27735
+ "google": "gemini-cli",
27736
+ // Codex variants
27737
+ "codex": "codex-cli",
27738
+ "codex-cli": "codex-cli",
27739
+ "openai": "codex-cli",
27740
+ "gpt": "codex-cli",
27741
+ // ax-cli variants
27742
+ "ax": "ax-cli",
27743
+ "ax-cli": "ax-cli",
27744
+ "axcli": "ax-cli"
27745
+ };
27746
+ var LoopGuard = class {
27747
+ config;
27748
+ constructor(config = {}) {
27749
+ this.config = {
27750
+ maxDepth: config.maxDepth ?? 2,
27751
+ maxChainLength: config.maxChainLength ?? 5,
27752
+ blockSelfCalls: config.blockSelfCalls ?? true,
27753
+ blockedPatterns: config.blockedPatterns ?? DEFAULT_BLOCKED_PATTERNS
27754
+ };
27755
+ if (this.config.maxDepth < 1 || this.config.maxDepth > 10) {
27756
+ throw new Error("LoopGuard: maxDepth must be between 1 and 10");
27757
+ }
27758
+ if (this.config.maxChainLength < 2 || this.config.maxChainLength > 20) {
27759
+ throw new Error("LoopGuard: maxChainLength must be between 2 and 20");
27760
+ }
27761
+ }
27762
+ /**
27763
+ * Validate that a task execution is allowed
27764
+ *
27765
+ * @param ctx - Current task context
27766
+ * @param targetEngine - Engine to route the task to
27767
+ * @throws {LoopPreventionError} If execution would cause a loop
27768
+ */
27769
+ validateExecution(ctx, targetEngine) {
27770
+ const normalizedTarget = this.normalizeClient(targetEngine);
27771
+ const projectedChain = [...ctx.callChain, "automatosx", normalizedTarget];
27772
+ if (ctx.depth >= this.config.maxDepth) {
27773
+ throw new LoopPreventionError(
27774
+ `Maximum depth exceeded: current depth ${ctx.depth} >= limit ${this.config.maxDepth}`,
27775
+ projectedChain,
27776
+ "DEPTH_EXCEEDED"
27777
+ );
27778
+ }
27779
+ if (this.config.blockSelfCalls) {
27780
+ const engineInChain = ctx.callChain.some(
27781
+ (entry) => this.normalizeClient(entry) === normalizedTarget
27782
+ );
27783
+ if (engineInChain) {
27784
+ throw new LoopPreventionError(
27785
+ `Circular call detected: ${normalizedTarget} already in chain [${ctx.callChain.join(" \u2192 ")}]`,
27786
+ projectedChain,
27787
+ "LOOP_DETECTED"
27788
+ );
27789
+ }
27790
+ }
27791
+ if (projectedChain.length > this.config.maxChainLength) {
27792
+ throw new LoopPreventionError(
27793
+ `Call chain too long: ${projectedChain.length} > ${this.config.maxChainLength}`,
27794
+ projectedChain,
27795
+ "CHAIN_TOO_LONG"
27796
+ );
27797
+ }
27798
+ const chainString = projectedChain.join("\u2192");
27799
+ for (const pattern of this.config.blockedPatterns) {
27800
+ if (pattern.test(chainString)) {
27801
+ throw new LoopPreventionError(
27802
+ `Blocked pattern matched: ${chainString} matches ${pattern.toString()}`,
27803
+ projectedChain,
27804
+ "BLOCKED_PATTERN"
27805
+ );
27806
+ }
27807
+ }
27808
+ }
27809
+ /**
27810
+ * Create a new task context for an incoming request
27811
+ *
27812
+ * @param originClient - The client that initiated the request
27813
+ * @param taskId - Optional task ID (will be set later if not provided)
27814
+ * @returns New task context
27815
+ */
27816
+ createContext(originClient, taskId = "") {
27817
+ const normalized = this.normalizeClient(originClient);
27818
+ return {
27819
+ taskId,
27820
+ originClient: normalized,
27821
+ callChain: [normalized],
27822
+ depth: 0,
27823
+ maxDepth: this.config.maxDepth,
27824
+ createdAt: Date.now()
27825
+ };
27826
+ }
27827
+ /**
27828
+ * Extend a task context for a nested call
27829
+ *
27830
+ * @param ctx - Current task context
27831
+ * @param engine - Engine being called
27832
+ * @returns Extended task context
27833
+ */
27834
+ extendContext(ctx, engine) {
27835
+ const normalizedEngine = this.normalizeClient(engine);
27836
+ return {
27837
+ ...ctx,
27838
+ callChain: [...ctx.callChain, "automatosx", normalizedEngine],
27839
+ depth: ctx.depth + 1,
27840
+ createdAt: Date.now()
27841
+ };
27842
+ }
27843
+ /**
27844
+ * Merge an incoming context with the current context
27845
+ * Used when a task already has context from a previous hop
27846
+ *
27847
+ * @param incomingCtx - Context from incoming request
27848
+ * @param currentOrigin - Current origin (automatosx)
27849
+ * @returns Merged context
27850
+ */
27851
+ mergeContext(incomingCtx, currentOrigin = "automatosx") {
27852
+ const callChain = incomingCtx.callChain ?? [];
27853
+ const originClient = incomingCtx.originClient ?? "unknown";
27854
+ const mergedChain = callChain.length > 0 ? [...callChain, currentOrigin] : [originClient, currentOrigin];
27855
+ return {
27856
+ taskId: incomingCtx.taskId ?? "",
27857
+ originClient: this.normalizeClient(originClient),
27858
+ callChain: mergedChain,
27859
+ depth: (incomingCtx.depth ?? 0) + 1,
27860
+ maxDepth: this.config.maxDepth,
27861
+ createdAt: Date.now()
27862
+ };
27863
+ }
27864
+ /**
27865
+ * Get a human-readable representation of the call chain
27866
+ *
27867
+ * @param ctx - Task context
27868
+ * @returns Formatted call chain string
27869
+ */
27870
+ getCallChainString(ctx) {
27871
+ return ctx.callChain.join(" \u2192 ");
27872
+ }
27873
+ /**
27874
+ * Check if a context is valid (not expired or corrupted)
27875
+ *
27876
+ * @param ctx - Task context to validate
27877
+ * @returns true if context is valid
27878
+ */
27879
+ isValidContext(ctx) {
27880
+ if (!ctx || typeof ctx !== "object") {
27881
+ return false;
27882
+ }
27883
+ const context = ctx;
27884
+ 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";
27885
+ }
27886
+ /**
27887
+ * Get current configuration
27888
+ */
27889
+ getConfig() {
27890
+ return { ...this.config };
27891
+ }
27892
+ /**
27893
+ * Normalize client name to standard format
27894
+ *
27895
+ * IMPORTANT: Unknown engines are preserved as-is (normalized formatting only)
27896
+ * to prevent false-positive loop detection when two different unknown engines
27897
+ * would both map to 'unknown' and trigger a circular call error.
27898
+ */
27899
+ normalizeClient(client) {
27900
+ if (!client || typeof client !== "string") {
27901
+ return "unknown";
27902
+ }
27903
+ const normalized = client.toLowerCase().trim().replace(/[\s-_]+/g, "-");
27904
+ if (CLIENT_NORMALIZATION_MAP[normalized]) {
27905
+ return CLIENT_NORMALIZATION_MAP[normalized];
27906
+ }
27907
+ return normalized;
27908
+ }
27909
+ };
27910
+ function createLoopGuard(config) {
27911
+ return new LoopGuard(config);
27912
+ }
27913
+
27914
+ // src/core/task-engine/compression.ts
27915
+ init_esm_shims();
27916
+ var DEFAULT_COMPRESSION_LEVEL = 6;
27917
+ var MIN_COMPRESSION_THRESHOLD = 4096;
27918
+ function decompressPayload(compressed) {
27919
+ try {
27920
+ const decompressed = gunzipSync(compressed);
27921
+ const json = decompressed.toString("utf-8");
27922
+ return JSON.parse(json);
27923
+ } catch (error) {
27924
+ throw new TaskEngineError(
27925
+ `Failed to decompress payload: ${error instanceof Error ? error.message : "Unknown error"}`,
27926
+ "COMPRESSION_ERROR",
27927
+ { originalError: error }
27928
+ );
27929
+ }
27930
+ }
27931
+ function compressWithInfo(payload, options = {}) {
27932
+ const level = options.level ?? DEFAULT_COMPRESSION_LEVEL;
27933
+ const minThreshold = options.minThreshold ?? MIN_COMPRESSION_THRESHOLD;
27934
+ try {
27935
+ const json = JSON.stringify(payload);
27936
+ const originalBuffer = Buffer.from(json, "utf-8");
27937
+ const originalSize = originalBuffer.length;
27938
+ if (originalSize < minThreshold) {
27939
+ return {
27940
+ data: originalBuffer,
27941
+ originalSize,
27942
+ compressedSize: originalSize,
27943
+ ratio: 1,
27944
+ compressed: false
27945
+ };
27946
+ }
27947
+ const compressedBuffer = gzipSync(originalBuffer, {
27948
+ level: Math.min(Math.max(level, 1), 9)
27949
+ });
27950
+ const compressedSize = compressedBuffer.length;
27951
+ if (compressedSize >= originalSize) {
27952
+ return {
27953
+ data: originalBuffer,
27954
+ originalSize,
27955
+ compressedSize: originalSize,
27956
+ ratio: 1,
27957
+ compressed: false
27958
+ };
27959
+ }
27960
+ return {
27961
+ data: compressedBuffer,
27962
+ originalSize,
27963
+ compressedSize,
27964
+ ratio: originalSize / compressedSize,
27965
+ compressed: true
27966
+ };
27967
+ } catch (error) {
27968
+ throw new TaskEngineError(
27969
+ `Failed to compress payload: ${error instanceof Error ? error.message : "Unknown error"}`,
27970
+ "COMPRESSION_ERROR",
27971
+ { originalError: error }
27972
+ );
27973
+ }
27974
+ }
27975
+
27976
+ // src/core/task-engine/store.ts
27977
+ init_esm_shims();
27978
+ init_factory();
27979
+ init_logger();
27980
+ var DEFAULT_CONFIG3 = {
27981
+ dbPath: ".automatosx/tasks/tasks.db",
27982
+ maxPayloadBytes: 1024 * 1024,
27983
+ // 1MB
27984
+ compressionEnabled: true,
27985
+ compressionLevel: 6,
27986
+ defaultTtlHours: 24,
27987
+ maxTtlHours: 168,
27988
+ // 7 days
27989
+ busyTimeout: 5e3
27990
+ };
27991
+ var SQL = {
27992
+ CREATE_TABLE: `
27993
+ CREATE TABLE IF NOT EXISTS task_store (
27994
+ id TEXT PRIMARY KEY,
27995
+ type TEXT NOT NULL CHECK (type IN ('web_search', 'code_review', 'code_generation', 'analysis', 'custom')),
27996
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'completed', 'failed', 'expired')),
27997
+ engine TEXT CHECK (engine IN ('gemini', 'claude', 'codex', 'ax-cli', NULL)),
27998
+ priority INTEGER NOT NULL DEFAULT 5 CHECK (priority BETWEEN 1 AND 10),
27999
+
28000
+ payload_compressed BLOB NOT NULL,
28001
+ payload_size_bytes INTEGER NOT NULL,
28002
+ payload_hash TEXT NOT NULL,
28003
+ is_compressed INTEGER NOT NULL DEFAULT 1,
28004
+
28005
+ result_compressed BLOB,
28006
+ result_size_bytes INTEGER,
28007
+ result_is_compressed INTEGER,
28008
+
28009
+ origin_client TEXT NOT NULL,
28010
+ call_chain TEXT NOT NULL,
28011
+ depth INTEGER NOT NULL DEFAULT 0,
28012
+
28013
+ created_at INTEGER NOT NULL,
28014
+ started_at INTEGER,
28015
+ completed_at INTEGER,
28016
+ expires_at INTEGER NOT NULL,
28017
+
28018
+ duration_ms INTEGER,
28019
+ tokens_prompt INTEGER,
28020
+ tokens_completion INTEGER,
28021
+ retry_count INTEGER NOT NULL DEFAULT 0,
28022
+
28023
+ error_code TEXT,
28024
+ error_message TEXT
28025
+ )
28026
+ `,
28027
+ CREATE_INDEXES: `
28028
+ CREATE INDEX IF NOT EXISTS idx_task_status ON task_store(status);
28029
+ CREATE INDEX IF NOT EXISTS idx_task_status_priority ON task_store(status, priority DESC);
28030
+ CREATE INDEX IF NOT EXISTS idx_task_expires ON task_store(expires_at);
28031
+ CREATE INDEX IF NOT EXISTS idx_task_engine ON task_store(engine);
28032
+ CREATE INDEX IF NOT EXISTS idx_task_created ON task_store(created_at DESC);
28033
+ CREATE INDEX IF NOT EXISTS idx_task_payload_hash ON task_store(payload_hash);
28034
+ CREATE INDEX IF NOT EXISTS idx_task_origin ON task_store(origin_client);
28035
+ `,
28036
+ INSERT_TASK: `
28037
+ INSERT INTO task_store (
28038
+ id, type, status, engine, priority,
28039
+ payload_compressed, payload_size_bytes, payload_hash, is_compressed,
28040
+ origin_client, call_chain, depth,
28041
+ created_at, expires_at
28042
+ ) VALUES (
28043
+ :id, :type, :status, :engine, :priority,
28044
+ :payload_compressed, :payload_size_bytes, :payload_hash, :is_compressed,
28045
+ :origin_client, :call_chain, :depth,
28046
+ :created_at, :expires_at
28047
+ )
28048
+ `,
28049
+ GET_BY_ID: `
28050
+ SELECT * FROM task_store WHERE id = ?
28051
+ `,
28052
+ UPDATE_STATUS: `
28053
+ UPDATE task_store SET
28054
+ status = :status,
28055
+ started_at = COALESCE(:started_at, started_at),
28056
+ completed_at = COALESCE(:completed_at, completed_at),
28057
+ error_code = COALESCE(:error_code, error_code),
28058
+ error_message = COALESCE(:error_message, error_message)
28059
+ WHERE id = :id
28060
+ `,
28061
+ UPDATE_RESULT: `
28062
+ UPDATE task_store SET
28063
+ status = 'completed',
28064
+ result_compressed = :result_compressed,
28065
+ result_size_bytes = :result_size_bytes,
28066
+ result_is_compressed = :result_is_compressed,
28067
+ completed_at = :completed_at,
28068
+ duration_ms = :duration_ms,
28069
+ tokens_prompt = :tokens_prompt,
28070
+ tokens_completion = :tokens_completion
28071
+ WHERE id = :id
28072
+ `,
28073
+ UPDATE_FAILED: `
28074
+ UPDATE task_store SET
28075
+ status = 'failed',
28076
+ completed_at = :completed_at,
28077
+ duration_ms = :duration_ms,
28078
+ error_code = :error_code,
28079
+ error_message = :error_message,
28080
+ retry_count = retry_count + 1
28081
+ WHERE id = :id
28082
+ `,
28083
+ INCREMENT_RETRY: `
28084
+ UPDATE task_store SET retry_count = retry_count + 1 WHERE id = ?
28085
+ `,
28086
+ DELETE_TASK: `
28087
+ DELETE FROM task_store WHERE id = ?
28088
+ `,
28089
+ CLEANUP_EXPIRED: `
28090
+ DELETE FROM task_store WHERE expires_at < ? AND status NOT IN ('running')
28091
+ `,
28092
+ CLEANUP_ZOMBIE_RUNNING: `
28093
+ UPDATE task_store SET
28094
+ status = 'failed',
28095
+ completed_at = :completed_at,
28096
+ error_code = 'ZOMBIE_TASK',
28097
+ error_message = 'Task was stuck in running state past expiry'
28098
+ WHERE status = 'running' AND expires_at < :now
28099
+ `,
28100
+ COUNT_BY_STATUS: `
28101
+ SELECT status, COUNT(*) as count FROM task_store GROUP BY status
28102
+ `,
28103
+ FIND_BY_HASH: `
28104
+ SELECT id FROM task_store WHERE payload_hash = ? AND status = 'completed' LIMIT 1
28105
+ `
28106
+ };
28107
+ var TaskStore = class {
28108
+ db;
28109
+ config;
28110
+ closed = false;
28111
+ // Prepared statements
28112
+ stmtInsert;
28113
+ stmtGetById;
28114
+ stmtUpdateStatus;
28115
+ stmtUpdateResult;
28116
+ stmtUpdateFailed;
28117
+ stmtIncrementRetry;
28118
+ stmtDelete;
28119
+ stmtCleanup;
28120
+ stmtCleanupZombies;
28121
+ stmtCountByStatus;
28122
+ stmtFindByHash;
28123
+ constructor(config = {}) {
28124
+ this.config = { ...DEFAULT_CONFIG3, ...config };
28125
+ this.db = DatabaseFactory.create(this.config.dbPath, {
28126
+ busyTimeout: this.config.busyTimeout,
28127
+ enableWal: true
28128
+ });
28129
+ this.initializeSchema();
28130
+ this.prepareStatements();
28131
+ logger.debug("TaskStore initialized", {
28132
+ dbPath: this.config.dbPath,
28133
+ maxPayloadBytes: this.config.maxPayloadBytes,
28134
+ compressionEnabled: this.config.compressionEnabled
28135
+ });
28136
+ }
28137
+ /**
28138
+ * Create a new task
28139
+ */
28140
+ createTask(input) {
28141
+ this.ensureOpen();
28142
+ const validated = CreateTaskInputSchema.parse(input);
28143
+ const payloadJson = JSON.stringify(validated.payload);
28144
+ const payloadSize = Buffer.byteLength(payloadJson, "utf-8");
28145
+ if (payloadSize > this.config.maxPayloadBytes) {
28146
+ throw new TaskEngineError(
28147
+ `Payload size ${payloadSize} exceeds limit ${this.config.maxPayloadBytes}`,
28148
+ "PAYLOAD_TOO_LARGE",
28149
+ { payloadSize, limit: this.config.maxPayloadBytes }
28150
+ );
28151
+ }
28152
+ let payloadBuffer;
28153
+ let isCompressed;
28154
+ let compressionRatio;
28155
+ if (this.config.compressionEnabled) {
28156
+ const result = compressWithInfo(validated.payload, {
28157
+ level: this.config.compressionLevel
28158
+ });
28159
+ payloadBuffer = result.data;
28160
+ isCompressed = result.compressed;
28161
+ compressionRatio = result.ratio;
28162
+ } else {
28163
+ payloadBuffer = Buffer.from(payloadJson, "utf-8");
28164
+ isCompressed = false;
28165
+ compressionRatio = 1;
28166
+ }
28167
+ const taskId = this.generateTaskId();
28168
+ const payloadHash = this.hashPayload(payloadJson);
28169
+ const now = Date.now();
28170
+ const ttlHours = Math.min(
28171
+ validated.ttlHours ?? this.config.defaultTtlHours,
28172
+ this.config.maxTtlHours
28173
+ );
28174
+ const expiresAt = now + ttlHours * 60 * 60 * 1e3;
28175
+ const estimatedEngine = validated.engine === "auto" ? this.estimateEngine(validated.type) : validated.engine;
28176
+ try {
28177
+ this.stmtInsert.run({
28178
+ id: taskId,
28179
+ type: validated.type,
28180
+ status: "pending",
28181
+ engine: validated.engine === "auto" ? null : validated.engine,
28182
+ priority: validated.priority ?? 5,
28183
+ payload_compressed: payloadBuffer,
28184
+ payload_size_bytes: payloadSize,
28185
+ payload_hash: payloadHash,
28186
+ is_compressed: isCompressed ? 1 : 0,
28187
+ origin_client: validated.context?.originClient ?? "unknown",
28188
+ call_chain: JSON.stringify(validated.context?.callChain ?? []),
28189
+ depth: validated.context?.depth ?? 0,
28190
+ created_at: now,
28191
+ expires_at: expiresAt
28192
+ });
28193
+ } catch (error) {
28194
+ throw new TaskEngineError(
28195
+ `Failed to create task: ${error instanceof Error ? error.message : "Unknown error"}`,
28196
+ "STORE_ERROR",
28197
+ { originalError: error }
28198
+ );
28199
+ }
28200
+ logger.debug("Task created", {
28201
+ taskId,
28202
+ type: validated.type,
28203
+ payloadSize,
28204
+ compressedSize: payloadBuffer.length,
28205
+ compressionRatio: compressionRatio.toFixed(2)
28206
+ });
28207
+ return {
28208
+ id: taskId,
28209
+ status: "pending",
28210
+ estimatedEngine: estimatedEngine ?? null,
28211
+ expiresAt,
28212
+ payloadSize,
28213
+ compressionRatio
28214
+ };
28215
+ }
28216
+ /**
28217
+ * Get a task by ID
28218
+ */
28219
+ getTask(taskId) {
28220
+ this.ensureOpen();
28221
+ const row = this.stmtGetById.get(taskId);
28222
+ if (!row) {
28223
+ return null;
28224
+ }
28225
+ return this.rowToTask(row);
28226
+ }
28227
+ /**
28228
+ * Update task status
28229
+ */
28230
+ updateTaskStatus(taskId, status, error) {
28231
+ this.ensureOpen();
28232
+ const now = Date.now();
28233
+ const params = {
28234
+ id: taskId,
28235
+ status,
28236
+ started_at: status === "running" ? now : null,
28237
+ completed_at: status === "completed" || status === "failed" ? now : null,
28238
+ error_code: error?.code ?? null,
28239
+ error_message: error?.message ?? null
28240
+ };
28241
+ const result = this.stmtUpdateStatus.run(params);
28242
+ if (result.changes === 0) {
28243
+ throw new TaskEngineError(
28244
+ `Task not found: ${taskId}`,
28245
+ "TASK_NOT_FOUND"
28246
+ );
28247
+ }
28248
+ logger.debug("Task status updated", { taskId, status });
28249
+ }
28250
+ /**
28251
+ * Update task with successful result
28252
+ */
28253
+ updateTaskResult(taskId, result, metrics) {
28254
+ this.ensureOpen();
28255
+ const now = Date.now();
28256
+ const resultJson = JSON.stringify(result);
28257
+ const resultSize = Buffer.byteLength(resultJson, "utf-8");
28258
+ let resultBuffer;
28259
+ let resultIsCompressed;
28260
+ if (this.config.compressionEnabled) {
28261
+ const compressed = compressWithInfo(result, {
28262
+ level: this.config.compressionLevel
28263
+ });
28264
+ resultBuffer = compressed.data;
28265
+ resultIsCompressed = compressed.compressed;
28266
+ } else {
28267
+ resultBuffer = Buffer.from(resultJson, "utf-8");
28268
+ resultIsCompressed = false;
28269
+ }
28270
+ const dbResult = this.stmtUpdateResult.run({
28271
+ id: taskId,
28272
+ result_compressed: resultBuffer,
28273
+ result_size_bytes: resultSize,
28274
+ result_is_compressed: resultIsCompressed ? 1 : 0,
28275
+ completed_at: now,
28276
+ duration_ms: metrics.durationMs ?? null,
28277
+ tokens_prompt: metrics.tokensPrompt ?? null,
28278
+ tokens_completion: metrics.tokensCompletion ?? null
28279
+ });
28280
+ if (dbResult.changes === 0) {
28281
+ throw new TaskEngineError(
28282
+ `Task not found: ${taskId}`,
28283
+ "TASK_NOT_FOUND"
28284
+ );
28285
+ }
28286
+ logger.debug("Task result updated", {
28287
+ taskId,
28288
+ resultSize,
28289
+ durationMs: metrics.durationMs
28290
+ });
28291
+ }
28292
+ /**
28293
+ * Update task with failure
28294
+ */
28295
+ updateTaskFailed(taskId, error, durationMs) {
28296
+ this.ensureOpen();
28297
+ const now = Date.now();
28298
+ const result = this.stmtUpdateFailed.run({
28299
+ id: taskId,
28300
+ completed_at: now,
28301
+ duration_ms: durationMs ?? null,
28302
+ error_code: error.code,
28303
+ error_message: error.message
28304
+ });
28305
+ if (result.changes === 0) {
28306
+ throw new TaskEngineError(
28307
+ `Task not found: ${taskId}`,
28308
+ "TASK_NOT_FOUND"
28309
+ );
28310
+ }
28311
+ logger.debug("Task marked as failed", { taskId, error: error.code });
28312
+ }
28313
+ /**
28314
+ * Increment retry count
28315
+ */
28316
+ incrementRetry(taskId) {
28317
+ this.ensureOpen();
28318
+ this.stmtIncrementRetry.run(taskId);
28319
+ }
28320
+ /**
28321
+ * Delete a task
28322
+ */
28323
+ deleteTask(taskId) {
28324
+ this.ensureOpen();
28325
+ const result = this.stmtDelete.run(taskId);
28326
+ const deleted = result.changes > 0;
28327
+ if (deleted) {
28328
+ logger.debug("Task deleted", { taskId });
28329
+ }
28330
+ return deleted;
28331
+ }
28332
+ /**
28333
+ * List tasks with optional filtering
28334
+ */
28335
+ listTasks(filter = {}) {
28336
+ this.ensureOpen();
28337
+ const validated = TaskFilterSchema.parse(filter);
28338
+ const conditions = ["1=1"];
28339
+ const params = {};
28340
+ if (validated.status) {
28341
+ conditions.push("status = :status");
28342
+ params.status = validated.status;
28343
+ }
28344
+ if (validated.engine) {
28345
+ conditions.push("engine = :engine");
28346
+ params.engine = validated.engine;
28347
+ }
28348
+ if (validated.type) {
28349
+ conditions.push("type = :type");
28350
+ params.type = validated.type;
28351
+ }
28352
+ if (validated.originClient) {
28353
+ conditions.push("origin_client = :origin_client");
28354
+ params.origin_client = validated.originClient;
28355
+ }
28356
+ const query = `
28357
+ SELECT * FROM task_store
28358
+ WHERE ${conditions.join(" AND ")}
28359
+ ORDER BY priority DESC, created_at ASC
28360
+ LIMIT :limit OFFSET :offset
28361
+ `;
28362
+ params.limit = validated.limit ?? 20;
28363
+ params.offset = validated.offset ?? 0;
28364
+ const stmt = this.db.prepare(query);
28365
+ const rows = stmt.all(params);
28366
+ return rows.map((row) => this.rowToTask(row));
28367
+ }
28368
+ /**
28369
+ * Count tasks by status
28370
+ */
28371
+ countByStatus() {
28372
+ this.ensureOpen();
28373
+ const rows = this.stmtCountByStatus.all();
28374
+ const counts = {
28375
+ pending: 0,
28376
+ running: 0,
28377
+ completed: 0,
28378
+ failed: 0,
28379
+ expired: 0
28380
+ };
28381
+ for (const row of rows) {
28382
+ counts[row.status] = row.count;
28383
+ }
28384
+ return counts;
28385
+ }
28386
+ /**
28387
+ * Count tasks matching filter (for pagination)
28388
+ */
28389
+ countTasks(filter = {}) {
28390
+ this.ensureOpen();
28391
+ const validated = TaskFilterSchema.parse(filter);
28392
+ const conditions = ["1=1"];
28393
+ const params = {};
28394
+ if (validated.status) {
28395
+ conditions.push("status = :status");
28396
+ params.status = validated.status;
28397
+ }
28398
+ if (validated.engine) {
28399
+ conditions.push("engine = :engine");
28400
+ params.engine = validated.engine;
28401
+ }
28402
+ if (validated.type) {
28403
+ conditions.push("type = :type");
28404
+ params.type = validated.type;
28405
+ }
28406
+ if (validated.originClient) {
28407
+ conditions.push("origin_client = :origin_client");
28408
+ params.origin_client = validated.originClient;
28409
+ }
28410
+ const query = `SELECT COUNT(*) as count FROM task_store WHERE ${conditions.join(" AND ")}`;
28411
+ const stmt = this.db.prepare(query);
28412
+ const row = stmt.get(params);
28413
+ return row.count;
28414
+ }
28415
+ /**
28416
+ * Find a completed task with the same payload (for caching)
28417
+ */
28418
+ findByPayloadHash(payloadHash) {
28419
+ this.ensureOpen();
28420
+ const row = this.stmtFindByHash.get(payloadHash);
28421
+ return row?.id ?? null;
28422
+ }
28423
+ /**
28424
+ * Cleanup expired tasks
28425
+ */
28426
+ cleanupExpired() {
28427
+ this.ensureOpen();
28428
+ const now = Date.now();
28429
+ const result = this.stmtCleanup.run(now);
28430
+ if (result.changes > 0) {
28431
+ logger.debug("Expired tasks cleaned up", { count: result.changes });
28432
+ }
28433
+ return result.changes;
28434
+ }
28435
+ /**
28436
+ * Mark zombie running tasks as failed.
28437
+ * These are tasks that have been in 'running' state past their expiry time,
28438
+ * likely due to process crashes or other failures.
28439
+ */
28440
+ cleanupZombieRunning() {
28441
+ this.ensureOpen();
28442
+ const now = Date.now();
28443
+ const result = this.stmtCleanupZombies.run({
28444
+ now,
28445
+ completed_at: now
28446
+ });
28447
+ if (result.changes > 0) {
28448
+ logger.warn("Zombie running tasks marked as failed", { count: result.changes });
28449
+ }
28450
+ return result.changes;
28451
+ }
28452
+ /**
28453
+ * Full cleanup: handles both expired non-running tasks and zombie running tasks.
28454
+ * Call this periodically to prevent resource leaks.
28455
+ */
28456
+ cleanupAll() {
28457
+ const zombies = this.cleanupZombieRunning();
28458
+ const expired = this.cleanupExpired();
28459
+ return { expired, zombies };
28460
+ }
28461
+ /**
28462
+ * Get store statistics
28463
+ */
28464
+ getStats() {
28465
+ this.ensureOpen();
28466
+ const counts = this.countByStatus();
28467
+ const totalTasks = Object.values(counts).reduce((a, b) => a + b, 0);
28468
+ let dbSizeBytes = 0;
28469
+ try {
28470
+ const pageCount = this.db.pragma("page_count", { simple: true });
28471
+ const pageSize = this.db.pragma("page_size", { simple: true });
28472
+ dbSizeBytes = pageCount * pageSize;
28473
+ } catch {
28474
+ }
28475
+ return {
28476
+ totalTasks,
28477
+ byStatus: counts,
28478
+ dbSizeBytes
28479
+ };
28480
+ }
28481
+ /**
28482
+ * Close the store
28483
+ */
28484
+ close() {
28485
+ if (this.closed) return;
28486
+ DatabaseFactory.close(this.db);
28487
+ this.closed = true;
28488
+ logger.debug("TaskStore closed");
28489
+ }
28490
+ // ============================================================================
28491
+ // Private Methods
28492
+ // ============================================================================
28493
+ initializeSchema() {
28494
+ this.db.exec(SQL.CREATE_TABLE);
28495
+ const indexStatements = SQL.CREATE_INDEXES.split(";").filter((s) => s.trim());
28496
+ for (const stmt of indexStatements) {
28497
+ this.db.exec(stmt);
28498
+ }
28499
+ }
28500
+ prepareStatements() {
28501
+ this.stmtInsert = this.db.prepare(SQL.INSERT_TASK);
28502
+ this.stmtGetById = this.db.prepare(SQL.GET_BY_ID);
28503
+ this.stmtUpdateStatus = this.db.prepare(SQL.UPDATE_STATUS);
28504
+ this.stmtUpdateResult = this.db.prepare(SQL.UPDATE_RESULT);
28505
+ this.stmtUpdateFailed = this.db.prepare(SQL.UPDATE_FAILED);
28506
+ this.stmtIncrementRetry = this.db.prepare(SQL.INCREMENT_RETRY);
28507
+ this.stmtDelete = this.db.prepare(SQL.DELETE_TASK);
28508
+ this.stmtCleanup = this.db.prepare(SQL.CLEANUP_EXPIRED);
28509
+ this.stmtCleanupZombies = this.db.prepare(SQL.CLEANUP_ZOMBIE_RUNNING);
28510
+ this.stmtCountByStatus = this.db.prepare(SQL.COUNT_BY_STATUS);
28511
+ this.stmtFindByHash = this.db.prepare(SQL.FIND_BY_HASH);
28512
+ }
28513
+ ensureOpen() {
28514
+ if (this.closed) {
28515
+ throw new TaskEngineError("TaskStore is closed", "STORE_ERROR");
28516
+ }
28517
+ }
28518
+ generateTaskId() {
28519
+ const timestamp = Date.now().toString(36);
28520
+ const random = randomBytes(4).toString("hex");
28521
+ return `task_${timestamp}${random}`;
28522
+ }
28523
+ hashPayload(json) {
28524
+ return createHash("sha256").update(json).digest("hex").substring(0, 16);
28525
+ }
28526
+ estimateEngine(type) {
28527
+ switch (type) {
28528
+ case "web_search":
28529
+ return "gemini";
28530
+ // Gemini is good for web search
28531
+ case "code_review":
28532
+ case "code_generation":
28533
+ return "claude";
28534
+ // Claude is good for code
28535
+ case "analysis":
28536
+ return "claude";
28537
+ // Claude is good for analysis
28538
+ case "custom":
28539
+ default:
28540
+ return "auto";
28541
+ }
28542
+ }
28543
+ rowToTask(row) {
28544
+ let payload;
28545
+ try {
28546
+ if (row.is_compressed) {
28547
+ payload = decompressPayload(row.payload_compressed);
28548
+ } else {
28549
+ payload = JSON.parse(row.payload_compressed.toString("utf-8"));
28550
+ }
28551
+ } catch (error) {
28552
+ logger.error("Failed to decompress task payload", {
28553
+ taskId: row.id,
28554
+ error: error instanceof Error ? error.message : String(error)
28555
+ });
28556
+ throw new TaskEngineError(
28557
+ `Failed to decompress task payload for task ${row.id}: ${error instanceof Error ? error.message : "Unknown error"}`,
28558
+ "STORE_ERROR",
28559
+ { taskId: row.id, originalError: error }
28560
+ );
28561
+ }
28562
+ let result = null;
28563
+ if (row.result_compressed) {
28564
+ try {
28565
+ if (row.result_is_compressed) {
28566
+ result = decompressPayload(row.result_compressed);
28567
+ } else {
28568
+ result = JSON.parse(row.result_compressed.toString("utf-8"));
28569
+ }
28570
+ } catch (error) {
28571
+ logger.warn("Failed to decompress task result, returning null", {
28572
+ taskId: row.id,
28573
+ error: error instanceof Error ? error.message : String(error)
28574
+ });
28575
+ result = null;
28576
+ }
28577
+ }
28578
+ return {
28579
+ id: row.id,
28580
+ type: row.type,
28581
+ status: row.status,
28582
+ engine: row.engine,
28583
+ priority: row.priority,
28584
+ payload,
28585
+ payloadSize: row.payload_size_bytes,
28586
+ result,
28587
+ context: {
28588
+ originClient: row.origin_client,
28589
+ callChain: JSON.parse(row.call_chain),
28590
+ depth: row.depth
28591
+ },
28592
+ createdAt: row.created_at,
28593
+ startedAt: row.started_at,
28594
+ completedAt: row.completed_at,
28595
+ expiresAt: row.expires_at,
28596
+ metrics: row.duration_ms != null ? {
28597
+ durationMs: row.duration_ms,
28598
+ tokensPrompt: row.tokens_prompt,
28599
+ tokensCompletion: row.tokens_completion
28600
+ } : null,
28601
+ error: row.error_code ? {
28602
+ code: row.error_code,
28603
+ message: row.error_message ?? ""
28604
+ } : null,
28605
+ retryCount: row.retry_count
28606
+ };
28607
+ }
28608
+ };
28609
+ function createTaskStore(config) {
28610
+ return new TaskStore(config);
28611
+ }
28612
+
28613
+ // src/core/task-engine/engine.ts
28614
+ init_esm_shims();
28615
+ init_logger();
28616
+ var DEFAULT_CONFIG4 = {
28617
+ store: {},
28618
+ loopGuard: {},
28619
+ maxConcurrent: 50,
28620
+ // Phase 5: Increased from 36 to 50
28621
+ defaultTimeoutMs: 12e4,
28622
+ maxRetries: 3,
28623
+ retryDelayMs: 1e3,
28624
+ cacheEnabled: true,
28625
+ cacheTtlMs: 36e5
28626
+ // 1 hour
28627
+ };
28628
+ var TaskEngine = class extends EventEmitter {
28629
+ config;
28630
+ loopGuard;
28631
+ store;
28632
+ runningTasks = /* @__PURE__ */ new Map();
28633
+ closed = false;
28634
+ // Statistics
28635
+ stats = {
28636
+ totalCreated: 0,
28637
+ completed: 0,
28638
+ failed: 0,
28639
+ expired: 0,
28640
+ cacheHits: 0,
28641
+ cacheMisses: 0,
28642
+ totalDurationMs: 0
28643
+ };
28644
+ constructor(config = {}) {
28645
+ super();
28646
+ this.config = {
28647
+ ...DEFAULT_CONFIG4,
28648
+ ...config,
28649
+ store: { ...DEFAULT_CONFIG4.store, ...config.store },
28650
+ loopGuard: { ...DEFAULT_CONFIG4.loopGuard, ...config.loopGuard }
28651
+ };
28652
+ this.loopGuard = createLoopGuard(this.config.loopGuard);
28653
+ this.store = createTaskStore(this.config.store);
28654
+ logger.info("TaskEngine initialized", {
28655
+ maxConcurrent: this.config.maxConcurrent,
28656
+ defaultTimeoutMs: this.config.defaultTimeoutMs,
28657
+ cacheEnabled: this.config.cacheEnabled
28658
+ });
28659
+ }
28660
+ // ============================================================================
28661
+ // Public API
28662
+ // ============================================================================
28663
+ /**
28664
+ * Create a new task
28665
+ */
28666
+ async createTask(input) {
28667
+ this.ensureOpen();
28668
+ try {
28669
+ const result = this.store.createTask(input);
28670
+ this.stats.totalCreated++;
28671
+ this.emit("task:created", result);
28672
+ logger.debug("Task created via engine", { taskId: result.id });
28673
+ return result;
28674
+ } catch (error) {
28675
+ if (error instanceof TaskEngineError) {
28676
+ throw error;
28677
+ }
28678
+ throw new TaskEngineError(
28679
+ `Failed to create task: ${error instanceof Error ? error.message : "Unknown error"}`,
28680
+ "STORE_ERROR",
28681
+ { originalError: error }
28682
+ );
28683
+ }
28684
+ }
28685
+ /**
28686
+ * Run a task
28687
+ */
28688
+ async runTask(taskId, options = {}) {
28689
+ this.ensureOpen();
28690
+ if (this.runningTasks.size >= this.config.maxConcurrent) {
28691
+ throw new TaskEngineError(
28692
+ `Maximum concurrent tasks reached: ${this.config.maxConcurrent}`,
28693
+ "EXECUTION_FAILED",
28694
+ { runningCount: this.runningTasks.size, limit: this.config.maxConcurrent }
28695
+ );
28696
+ }
28697
+ const task = this.store.getTask(taskId);
28698
+ if (!task) {
28699
+ throw new TaskEngineError(`Task not found: ${taskId}`, "TASK_NOT_FOUND");
28700
+ }
28701
+ if (task.status === "running") {
28702
+ throw new TaskEngineError(
28703
+ `Task is already running: ${taskId}`,
28704
+ "TASK_ALREADY_RUNNING"
28705
+ );
28706
+ }
28707
+ if (task.status === "completed") {
28708
+ if (task.result) {
28709
+ this.stats.cacheHits++;
28710
+ return {
28711
+ taskId,
28712
+ status: "completed",
28713
+ result: task.result,
28714
+ engine: task.engine ?? "auto",
28715
+ metrics: task.metrics,
28716
+ error: null,
28717
+ cacheHit: true
28718
+ };
28719
+ }
28720
+ }
28721
+ if (task.status === "expired") {
28722
+ throw new TaskEngineError(`Task has expired: ${taskId}`, "TASK_EXPIRED");
28723
+ }
28724
+ if (Date.now() > task.expiresAt) {
28725
+ this.store.updateTaskStatus(taskId, "expired");
28726
+ throw new TaskEngineError(`Task has expired: ${taskId}`, "TASK_EXPIRED");
28727
+ }
28728
+ const targetEngine = options.engineOverride ?? task.engine ?? this.estimateEngine(task.type);
28729
+ const loopContext = this.buildLoopContext(task, options.loopContext);
28730
+ try {
28731
+ this.loopGuard.validateExecution(loopContext, targetEngine);
28732
+ } catch (error) {
28733
+ if (isLoopPreventionError(error)) {
28734
+ this.emit("loop:prevented", taskId, loopContext, targetEngine);
28735
+ logger.warn("Loop prevented", {
28736
+ taskId,
28737
+ targetEngine,
28738
+ callChain: error.callChain
28739
+ });
28740
+ }
28741
+ throw error;
28742
+ }
28743
+ return this.executeTask(task, targetEngine, loopContext, options);
28744
+ }
28745
+ /**
28746
+ * Get task result (without running)
28747
+ */
28748
+ getTaskResult(taskId) {
28749
+ this.ensureOpen();
28750
+ const task = this.store.getTask(taskId);
28751
+ if (!task) {
28752
+ return null;
28753
+ }
28754
+ if (task.status !== "completed") {
28755
+ return null;
28756
+ }
28757
+ return {
28758
+ taskId,
28759
+ status: "completed",
28760
+ result: task.result,
28761
+ engine: task.engine ?? "auto",
28762
+ metrics: task.metrics,
28763
+ error: null,
28764
+ cacheHit: true
28765
+ };
28766
+ }
28767
+ /**
28768
+ * Get a task by ID
28769
+ */
28770
+ getTask(taskId) {
28771
+ this.ensureOpen();
28772
+ return this.store.getTask(taskId);
28773
+ }
28774
+ /**
28775
+ * List tasks with pagination info
28776
+ */
28777
+ listTasks(filter) {
28778
+ this.ensureOpen();
28779
+ const tasks = this.store.listTasks(filter);
28780
+ const total = this.store.countTasks(filter);
28781
+ return { tasks, total };
28782
+ }
28783
+ /**
28784
+ * Delete a task
28785
+ */
28786
+ deleteTask(taskId, force = false) {
28787
+ this.ensureOpen();
28788
+ const task = this.store.getTask(taskId);
28789
+ if (!task) {
28790
+ return false;
28791
+ }
28792
+ if (task.status === "running" && !force) {
28793
+ throw new TaskEngineError(
28794
+ `Cannot delete running task: ${taskId}. Use force=true to override.`,
28795
+ "TASK_ALREADY_RUNNING"
28796
+ );
28797
+ }
28798
+ if (task.status === "running") {
28799
+ const controller = this.runningTasks.get(taskId);
28800
+ if (controller) {
28801
+ controller.abort();
28802
+ this.runningTasks.delete(taskId);
28803
+ }
28804
+ }
28805
+ return this.store.deleteTask(taskId);
28806
+ }
28807
+ /**
28808
+ * Get engine statistics
28809
+ */
28810
+ getStats() {
28811
+ this.ensureOpen();
28812
+ const storeStats = this.store.getStats();
28813
+ const cacheTotal = this.stats.cacheHits + this.stats.cacheMisses;
28814
+ return {
28815
+ totalCreated: this.stats.totalCreated,
28816
+ runningCount: this.runningTasks.size,
28817
+ completedCount: storeStats.byStatus.completed,
28818
+ failedCount: storeStats.byStatus.failed,
28819
+ expiredCount: storeStats.byStatus.expired,
28820
+ cache: {
28821
+ hits: this.stats.cacheHits,
28822
+ misses: this.stats.cacheMisses,
28823
+ hitRate: cacheTotal > 0 ? this.stats.cacheHits / cacheTotal : 0
28824
+ },
28825
+ avgDurationMs: this.stats.completed > 0 ? this.stats.totalDurationMs / this.stats.completed : 0
28826
+ };
28827
+ }
28828
+ /**
28829
+ * Cleanup expired tasks
28830
+ */
28831
+ cleanupExpired() {
28832
+ this.ensureOpen();
28833
+ const cleaned = this.store.cleanupExpired();
28834
+ this.stats.expired += cleaned;
28835
+ return cleaned;
28836
+ }
28837
+ /**
28838
+ * Shutdown the engine
28839
+ */
28840
+ async shutdown() {
28841
+ if (this.closed) return;
28842
+ logger.info("TaskEngine shutting down", {
28843
+ runningTasks: this.runningTasks.size
28844
+ });
28845
+ for (const [taskId, controller] of this.runningTasks) {
28846
+ controller.abort();
28847
+ logger.debug("Cancelled running task", { taskId });
28848
+ }
28849
+ this.runningTasks.clear();
28850
+ this.store.close();
28851
+ this.closed = true;
28852
+ logger.info("TaskEngine shutdown complete");
28853
+ }
28854
+ /**
28855
+ * Check if engine is healthy
28856
+ */
28857
+ isHealthy() {
28858
+ return !this.closed && this.runningTasks.size < this.config.maxConcurrent;
28859
+ }
28860
+ // ============================================================================
28861
+ // Private Methods
28862
+ // ============================================================================
28863
+ async executeTask(task, targetEngine, context, options) {
28864
+ const taskId = task.id;
28865
+ const abortController = new AbortController();
28866
+ const startTime = Date.now();
28867
+ this.runningTasks.set(taskId, abortController);
28868
+ this.store.updateTaskStatus(taskId, "running");
28869
+ this.emit("task:started", taskId, targetEngine);
28870
+ const timeoutMs = options.timeoutMs ?? this.config.defaultTimeoutMs;
28871
+ const timeoutId = setTimeout(() => {
28872
+ abortController.abort();
28873
+ }, timeoutMs);
28874
+ try {
28875
+ const result = await this.executeWithRetry(
28876
+ task,
28877
+ targetEngine,
28878
+ context,
28879
+ abortController.signal
28880
+ );
28881
+ const durationMs = Date.now() - startTime;
28882
+ this.store.updateTaskResult(taskId, result, {
28883
+ durationMs,
28884
+ tokensPrompt: null,
28885
+ tokensCompletion: null
28886
+ });
28887
+ this.stats.completed++;
28888
+ this.stats.cacheMisses++;
28889
+ this.stats.totalDurationMs += durationMs;
28890
+ const taskResult = {
28891
+ taskId,
28892
+ status: "completed",
28893
+ result,
28894
+ engine: targetEngine,
28895
+ metrics: { durationMs, tokensPrompt: null, tokensCompletion: null },
28896
+ error: null,
28897
+ cacheHit: false
28898
+ };
28899
+ this.emit("task:completed", taskId, taskResult);
28900
+ logger.debug("Task completed", { taskId, durationMs, engine: targetEngine });
28901
+ return taskResult;
28902
+ } catch (error) {
28903
+ const durationMs = Date.now() - startTime;
28904
+ const errorObj = error instanceof Error ? error : new Error(String(error));
28905
+ if (abortController.signal.aborted) {
28906
+ const taskError2 = { code: "TIMEOUT", message: `Task timed out after ${timeoutMs}ms` };
28907
+ this.store.updateTaskFailed(taskId, taskError2, durationMs);
28908
+ this.stats.failed++;
28909
+ this.emit("task:failed", taskId, new TaskEngineError(taskError2.message, "EXECUTION_TIMEOUT"));
28910
+ return {
28911
+ taskId,
28912
+ status: "failed",
28913
+ result: null,
28914
+ engine: targetEngine,
28915
+ metrics: { durationMs, tokensPrompt: null, tokensCompletion: null },
28916
+ error: taskError2,
28917
+ cacheHit: false
28918
+ };
28919
+ }
28920
+ const taskError = {
28921
+ code: error instanceof TaskEngineError ? error.code : "EXECUTION_FAILED",
28922
+ message: errorObj.message
28923
+ };
28924
+ this.store.updateTaskFailed(taskId, taskError, durationMs);
28925
+ this.stats.failed++;
28926
+ this.emit("task:failed", taskId, errorObj);
28927
+ logger.error("Task failed", { taskId, error: errorObj.message, durationMs });
28928
+ return {
28929
+ taskId,
28930
+ status: "failed",
28931
+ result: null,
28932
+ engine: targetEngine,
28933
+ metrics: { durationMs, tokensPrompt: null, tokensCompletion: null },
28934
+ error: taskError,
28935
+ cacheHit: false
28936
+ };
28937
+ } finally {
28938
+ clearTimeout(timeoutId);
28939
+ this.runningTasks.delete(taskId);
28940
+ }
28941
+ }
28942
+ async executeWithRetry(task, engine, context, signal) {
28943
+ let lastError;
28944
+ for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
28945
+ if (signal.aborted) {
28946
+ throw new TaskEngineError("Task execution aborted", "EXECUTION_TIMEOUT");
28947
+ }
28948
+ try {
28949
+ return await this.executeOnEngine(task, engine, context, signal);
28950
+ } catch (error) {
28951
+ lastError = error instanceof Error ? error : new Error(String(error));
28952
+ if (isLoopPreventionError(error)) {
28953
+ throw error;
28954
+ }
28955
+ if (signal.aborted) {
28956
+ throw error;
28957
+ }
28958
+ if (attempt < this.config.maxRetries && this.isRetryableError(error)) {
28959
+ const delay = this.config.retryDelayMs * Math.pow(2, attempt);
28960
+ this.store.incrementRetry(task.id);
28961
+ this.emit("task:retry", task.id, attempt + 1, lastError);
28962
+ logger.debug("Retrying task", {
28963
+ taskId: task.id,
28964
+ attempt: attempt + 1,
28965
+ delay,
28966
+ error: lastError.message
28967
+ });
28968
+ await this.sleep(delay, signal);
28969
+ continue;
28970
+ }
28971
+ throw error;
28972
+ }
28973
+ }
28974
+ throw lastError ?? new TaskEngineError("Task execution failed", "EXECUTION_FAILED");
28975
+ }
28976
+ /**
28977
+ * Execute task on target engine
28978
+ *
28979
+ * Phase 1: Simulated execution (returns mock result)
28980
+ * Phase 2+: Will integrate with Router for actual execution
28981
+ */
28982
+ async executeOnEngine(task, engine, context, signal) {
28983
+ logger.debug("Executing task on engine", {
28984
+ taskId: task.id,
28985
+ engine,
28986
+ type: task.type,
28987
+ callChain: context.callChain
28988
+ });
28989
+ await this.sleep(100, signal);
28990
+ switch (task.type) {
28991
+ case "web_search":
28992
+ return {
28993
+ query: task.payload.query ?? "unknown",
28994
+ results: [
28995
+ { title: "Result 1", url: "https://example.com/1", snippet: "Sample result 1" },
28996
+ { title: "Result 2", url: "https://example.com/2", snippet: "Sample result 2" }
28997
+ ],
28998
+ engine,
28999
+ timestamp: Date.now()
29000
+ };
29001
+ case "code_review":
29002
+ return {
29003
+ file: task.payload.file ?? "unknown.ts",
29004
+ issues: [],
29005
+ suggestions: ["Consider adding type annotations"],
29006
+ engine,
29007
+ timestamp: Date.now()
29008
+ };
29009
+ case "code_generation":
29010
+ return {
29011
+ prompt: task.payload.prompt ?? "",
29012
+ code: "// Generated code placeholder",
29013
+ language: task.payload.language ?? "typescript",
29014
+ engine,
29015
+ timestamp: Date.now()
29016
+ };
29017
+ case "analysis":
29018
+ return {
29019
+ input: task.payload.input ?? {},
29020
+ analysis: "Analysis result placeholder",
29021
+ confidence: 0.95,
29022
+ engine,
29023
+ timestamp: Date.now()
29024
+ };
29025
+ case "custom":
29026
+ default:
29027
+ return {
29028
+ payload: task.payload,
29029
+ result: "Custom task completed",
29030
+ engine,
29031
+ timestamp: Date.now()
29032
+ };
29033
+ }
29034
+ }
29035
+ buildLoopContext(task, incomingContext) {
29036
+ if (incomingContext) {
29037
+ return this.loopGuard.mergeContext(incomingContext);
29038
+ }
29039
+ return {
29040
+ taskId: task.id,
29041
+ originClient: task.context.originClient,
29042
+ callChain: task.context.callChain.length > 0 ? task.context.callChain : [task.context.originClient],
29043
+ depth: task.context.depth,
29044
+ maxDepth: this.loopGuard.getConfig().maxDepth,
29045
+ createdAt: task.createdAt
29046
+ };
29047
+ }
29048
+ estimateEngine(type) {
29049
+ switch (type) {
29050
+ case "web_search":
29051
+ return "gemini";
29052
+ case "code_review":
29053
+ case "code_generation":
29054
+ return "claude";
29055
+ case "analysis":
29056
+ return "claude";
29057
+ default:
29058
+ return "claude";
29059
+ }
29060
+ }
29061
+ isRetryableError(error) {
29062
+ if (error instanceof TaskEngineError) {
29063
+ const nonRetryable = [
29064
+ "TASK_NOT_FOUND",
29065
+ "TASK_EXPIRED",
29066
+ "PAYLOAD_TOO_LARGE",
29067
+ "LOOP_DETECTED",
29068
+ "DEPTH_EXCEEDED",
29069
+ "CHAIN_TOO_LONG",
29070
+ "BLOCKED_PATTERN"
29071
+ ];
29072
+ return !nonRetryable.includes(error.code);
29073
+ }
29074
+ return true;
29075
+ }
29076
+ ensureOpen() {
29077
+ if (this.closed) {
29078
+ throw new TaskEngineError("TaskEngine is closed", "EXECUTION_FAILED");
29079
+ }
29080
+ }
29081
+ sleep(ms, signal) {
29082
+ return new Promise((resolve13, reject) => {
29083
+ if (signal?.aborted) {
29084
+ reject(new Error("Aborted"));
29085
+ return;
29086
+ }
29087
+ const abortHandler = () => {
29088
+ clearTimeout(timeoutId);
29089
+ reject(new Error("Aborted"));
29090
+ };
29091
+ const timeoutId = setTimeout(() => {
29092
+ signal?.removeEventListener("abort", abortHandler);
29093
+ resolve13();
29094
+ }, ms);
29095
+ signal?.addEventListener("abort", abortHandler, { once: true });
29096
+ });
29097
+ }
29098
+ };
29099
+ var defaultTaskEngine = null;
29100
+ function getTaskEngine(config) {
29101
+ if (!defaultTaskEngine) {
29102
+ defaultTaskEngine = new TaskEngine(config);
29103
+ }
29104
+ return defaultTaskEngine;
29105
+ }
29106
+
29107
+ // src/core/task-engine/cache.ts
29108
+ init_esm_shims();
29109
+ init_logger();
29110
+
29111
+ // src/core/task-engine/write-batcher.ts
29112
+ init_esm_shims();
29113
+ init_logger();
29114
+
29115
+ // src/core/task-engine/request-coalescer.ts
29116
+ init_esm_shims();
29117
+ init_logger();
29118
+
29119
+ // src/core/task-engine/rate-limiter.ts
29120
+ init_esm_shims();
29121
+ init_logger();
29122
+
29123
+ // src/core/task-engine/audit-logger.ts
29124
+ init_esm_shims();
29125
+ init_logger();
29126
+
29127
+ // src/core/task-engine/circuit-breaker.ts
29128
+ init_esm_shims();
29129
+ init_logger();
29130
+
29131
+ // src/core/task-engine/write-pool.ts
29132
+ init_esm_shims();
29133
+ init_logger();
29134
+
29135
+ // src/core/task-engine/task-queue.ts
29136
+ init_esm_shims();
29137
+ init_logger();
29138
+
29139
+ // src/mcp/tools/task/create-task.ts
29140
+ init_logger();
29141
+ function createCreateTaskHandler(deps) {
29142
+ return async (input) => {
29143
+ const startTime = Date.now();
29144
+ logger.info("[create_task] Creating task", {
29145
+ type: input.type,
29146
+ engine: input.engine ?? "auto",
29147
+ priority: input.priority ?? 5,
29148
+ payloadSize: JSON.stringify(input.payload).length
29149
+ });
29150
+ try {
29151
+ const taskEngine = getTaskEngine();
29152
+ const session = deps.getSession();
29153
+ const taskInput = {
29154
+ type: input.type,
29155
+ payload: input.payload,
29156
+ engine: input.engine,
29157
+ priority: input.priority,
29158
+ ttlHours: input.ttl_hours,
29159
+ context: session ? {
29160
+ originClient: mapNormalizedProviderToOriginClient(session.normalizedProvider),
29161
+ callChain: [mapNormalizedProviderToOriginClient(session.normalizedProvider)],
29162
+ depth: 0
29163
+ } : void 0
29164
+ };
29165
+ const result = await taskEngine.createTask(taskInput);
29166
+ const output = {
29167
+ task_id: result.id,
29168
+ status: "pending",
29169
+ estimated_engine: result.estimatedEngine,
29170
+ expires_at: new Date(result.expiresAt).toISOString(),
29171
+ payload_size_bytes: result.payloadSize,
29172
+ compression_ratio: result.compressionRatio
29173
+ };
29174
+ logger.info("[create_task] Task created successfully", {
29175
+ taskId: result.id,
29176
+ estimatedEngine: result.estimatedEngine,
29177
+ durationMs: Date.now() - startTime
29178
+ });
29179
+ return output;
29180
+ } catch (error) {
29181
+ logger.error("[create_task] Failed to create task", {
29182
+ error: error instanceof Error ? error.message : String(error),
29183
+ type: input.type
29184
+ });
29185
+ throw error;
29186
+ }
29187
+ };
29188
+ }
29189
+ function mapNormalizedProviderToOriginClient(provider) {
29190
+ const mapping = {
29191
+ "claude": "claude-code",
29192
+ "gemini": "gemini-cli",
29193
+ "codex": "codex-cli",
29194
+ "ax-cli": "ax-cli",
29195
+ "unknown": "unknown"
29196
+ };
29197
+ return mapping[provider] ?? "unknown";
29198
+ }
29199
+ var createTaskSchema = {
29200
+ name: "create_task",
29201
+ description: "Create a new task with payload for deferred execution. Returns task_id for later execution via run_task.",
29202
+ inputSchema: {
29203
+ type: "object",
29204
+ properties: {
29205
+ type: {
29206
+ type: "string",
29207
+ enum: ["web_search", "code_review", "code_generation", "analysis", "custom"],
29208
+ description: "Task type for routing optimization"
29209
+ },
29210
+ payload: {
29211
+ type: "object",
29212
+ description: "Task data (max 1MB after JSON serialization)"
29213
+ },
29214
+ engine: {
29215
+ type: "string",
29216
+ enum: ["auto", "gemini", "claude", "codex", "ax-cli"],
29217
+ default: "auto",
29218
+ description: "Target engine (auto = router decides)"
29219
+ },
29220
+ priority: {
29221
+ type: "integer",
29222
+ minimum: 1,
29223
+ maximum: 10,
29224
+ default: 5,
29225
+ description: "Execution priority (1=lowest, 10=highest)"
29226
+ },
29227
+ ttl_hours: {
29228
+ type: "integer",
29229
+ minimum: 1,
29230
+ maximum: 168,
29231
+ default: 24,
29232
+ description: "Task time-to-live in hours"
29233
+ }
29234
+ },
29235
+ required: ["type", "payload"]
29236
+ }
29237
+ };
29238
+
29239
+ // src/mcp/tools/task/run-task.ts
29240
+ init_esm_shims();
29241
+ init_logger();
29242
+ function createRunTaskHandler(deps) {
29243
+ return async (input) => {
29244
+ const startTime = Date.now();
29245
+ logger.info("[run_task] Executing task", {
29246
+ taskId: input.task_id,
29247
+ engineOverride: input.engine_override,
29248
+ timeoutMs: input.timeout_ms,
29249
+ skipCache: input.skip_cache
29250
+ });
29251
+ try {
29252
+ const taskEngine = getTaskEngine();
29253
+ const session = deps.getSession();
29254
+ const options = {
29255
+ engineOverride: input.engine_override,
29256
+ timeoutMs: input.timeout_ms,
29257
+ skipCache: input.skip_cache
29258
+ };
29259
+ const result = await taskEngine.runTask(input.task_id, options);
29260
+ const output = {
29261
+ task_id: result.taskId,
29262
+ status: result.status,
29263
+ result: result.result,
29264
+ engine: result.engine,
29265
+ metrics: {
29266
+ duration_ms: result.metrics?.durationMs ?? 0,
29267
+ tokens_prompt: result.metrics?.tokensPrompt ?? null,
29268
+ tokens_completion: result.metrics?.tokensCompletion ?? null
29269
+ },
29270
+ cache_hit: result.cacheHit
29271
+ };
29272
+ if (result.error) {
29273
+ output.error = {
29274
+ code: result.error.code,
29275
+ message: result.error.message
29276
+ };
29277
+ }
29278
+ logger.info("[run_task] Task execution completed", {
29279
+ taskId: result.taskId,
29280
+ status: result.status,
29281
+ engine: result.engine,
29282
+ cacheHit: result.cacheHit,
29283
+ durationMs: Date.now() - startTime
29284
+ });
29285
+ return output;
29286
+ } catch (error) {
29287
+ logger.error("[run_task] Failed to execute task", {
29288
+ error: error instanceof Error ? error.message : String(error),
29289
+ taskId: input.task_id
29290
+ });
29291
+ throw error;
29292
+ }
29293
+ };
29294
+ }
29295
+ var runTaskSchema = {
29296
+ name: "run_task",
29297
+ description: "Execute a previously created task and return results. Blocks until completion or timeout.",
29298
+ inputSchema: {
29299
+ type: "object",
29300
+ properties: {
29301
+ task_id: {
29302
+ type: "string",
29303
+ description: "Task ID to execute"
29304
+ },
29305
+ engine_override: {
29306
+ type: "string",
29307
+ enum: ["gemini", "claude", "codex", "ax-cli"],
29308
+ description: "Override the estimated engine"
29309
+ },
29310
+ timeout_ms: {
29311
+ type: "integer",
29312
+ minimum: 1e3,
29313
+ maximum: 3e5,
29314
+ default: 3e4,
29315
+ description: "Custom timeout in milliseconds"
29316
+ },
29317
+ skip_cache: {
29318
+ type: "boolean",
29319
+ default: false,
29320
+ description: "Skip cache and force re-execution"
29321
+ }
29322
+ },
29323
+ required: ["task_id"]
29324
+ }
29325
+ };
29326
+
29327
+ // src/mcp/tools/task/get-task-result.ts
29328
+ init_esm_shims();
29329
+ init_logger();
29330
+ function createGetTaskResultHandler() {
29331
+ return async (input) => {
29332
+ logger.info("[get_task_result] Retrieving task", {
29333
+ taskId: input.task_id,
29334
+ includePayload: input.include_payload
29335
+ });
29336
+ try {
29337
+ const taskEngine = getTaskEngine();
29338
+ const task = taskEngine.getTask(input.task_id);
29339
+ if (!task) {
29340
+ throw new Error(`Task not found: ${input.task_id}`);
29341
+ }
29342
+ const output = {
29343
+ task_id: task.id,
29344
+ status: task.status,
29345
+ type: task.type,
29346
+ result: task.result,
29347
+ engine: task.engine,
29348
+ created_at: new Date(task.createdAt).toISOString(),
29349
+ completed_at: task.completedAt ? new Date(task.completedAt).toISOString() : null,
29350
+ expires_at: new Date(task.expiresAt).toISOString()
29351
+ };
29352
+ if (input.include_payload) {
29353
+ output.payload = task.payload;
29354
+ }
29355
+ if (task.error) {
29356
+ output.error = {
29357
+ code: task.error.code,
29358
+ message: task.error.message
29359
+ };
29360
+ }
29361
+ logger.info("[get_task_result] Task retrieved", {
29362
+ taskId: task.id,
29363
+ status: task.status
29364
+ });
29365
+ return output;
29366
+ } catch (error) {
29367
+ logger.error("[get_task_result] Failed to retrieve task", {
29368
+ error: error instanceof Error ? error.message : String(error),
29369
+ taskId: input.task_id
29370
+ });
29371
+ throw error;
29372
+ }
29373
+ };
29374
+ }
29375
+ var getTaskResultSchema = {
29376
+ name: "get_task_result",
29377
+ description: "Retrieve the result of a task. Does not execute - use run_task for execution.",
29378
+ inputSchema: {
29379
+ type: "object",
29380
+ properties: {
29381
+ task_id: {
29382
+ type: "string",
29383
+ description: "Task ID to retrieve"
29384
+ },
29385
+ include_payload: {
29386
+ type: "boolean",
29387
+ default: false,
29388
+ description: "Include original payload in response"
29389
+ }
29390
+ },
29391
+ required: ["task_id"]
29392
+ }
29393
+ };
29394
+
29395
+ // src/mcp/tools/task/list-tasks.ts
29396
+ init_esm_shims();
29397
+ init_logger();
29398
+ function createListTasksHandler() {
29399
+ return async (input) => {
29400
+ const limit = Math.min(input.limit ?? 20, 100);
29401
+ const offset = input.offset ?? 0;
29402
+ logger.info("[list_tasks] Listing tasks", {
29403
+ status: input.status,
29404
+ type: input.type,
29405
+ engine: input.engine,
29406
+ limit,
29407
+ offset
29408
+ });
29409
+ try {
29410
+ const taskEngine = getTaskEngine();
29411
+ const filter = {
29412
+ status: input.status,
29413
+ type: input.type,
29414
+ engine: input.engine,
29415
+ limit,
29416
+ offset
29417
+ };
29418
+ const result = taskEngine.listTasks(filter);
29419
+ const tasks = result.tasks.map((task) => ({
29420
+ task_id: task.id,
29421
+ type: task.type,
29422
+ status: task.status,
29423
+ engine: task.engine,
29424
+ priority: task.priority,
29425
+ created_at: new Date(task.createdAt).toISOString(),
29426
+ expires_at: new Date(task.expiresAt).toISOString(),
29427
+ has_result: task.result !== null
29428
+ }));
29429
+ const output = {
29430
+ tasks,
29431
+ total: result.total,
29432
+ offset,
29433
+ limit,
29434
+ has_more: offset + tasks.length < result.total
29435
+ };
29436
+ logger.info("[list_tasks] Tasks listed", {
29437
+ returned: tasks.length,
29438
+ total: result.total,
29439
+ hasMore: output.has_more
29440
+ });
29441
+ return output;
29442
+ } catch (error) {
29443
+ logger.error("[list_tasks] Failed to list tasks", {
29444
+ error: error instanceof Error ? error.message : String(error)
29445
+ });
29446
+ throw error;
29447
+ }
29448
+ };
29449
+ }
29450
+ var listTasksSchema = {
29451
+ name: "list_tasks",
29452
+ description: "List tasks with optional filtering. Supports pagination.",
29453
+ inputSchema: {
29454
+ type: "object",
29455
+ properties: {
29456
+ status: {
29457
+ type: "string",
29458
+ enum: ["pending", "running", "completed", "failed", "expired"],
29459
+ description: "Filter by task status"
29460
+ },
29461
+ type: {
29462
+ type: "string",
29463
+ enum: ["web_search", "code_review", "code_generation", "analysis", "custom"],
29464
+ description: "Filter by task type"
29465
+ },
29466
+ engine: {
29467
+ type: "string",
29468
+ enum: ["gemini", "claude", "codex", "ax-cli"],
29469
+ description: "Filter by engine"
29470
+ },
29471
+ limit: {
29472
+ type: "integer",
29473
+ minimum: 1,
29474
+ maximum: 100,
29475
+ default: 20,
29476
+ description: "Maximum results to return"
29477
+ },
29478
+ offset: {
29479
+ type: "integer",
29480
+ minimum: 0,
29481
+ default: 0,
29482
+ description: "Offset for pagination"
29483
+ }
29484
+ },
29485
+ required: []
29486
+ }
29487
+ };
29488
+
29489
+ // src/mcp/tools/task/delete-task.ts
29490
+ init_esm_shims();
29491
+ init_logger();
29492
+ function createDeleteTaskHandler() {
29493
+ return async (input) => {
29494
+ logger.info("[delete_task] Deleting task", {
29495
+ taskId: input.task_id,
29496
+ force: input.force
29497
+ });
29498
+ try {
29499
+ const taskEngine = getTaskEngine();
29500
+ const task = taskEngine.getTask(input.task_id);
29501
+ if (!task) {
29502
+ return {
29503
+ task_id: input.task_id,
29504
+ deleted: false,
29505
+ previous_status: "unknown",
29506
+ message: `Task not found: ${input.task_id}`
29507
+ };
29508
+ }
29509
+ if (task.status === "running" && !input.force) {
29510
+ return {
29511
+ task_id: input.task_id,
29512
+ deleted: false,
29513
+ previous_status: task.status,
29514
+ message: "Cannot delete running task. Use force=true to override."
29515
+ };
29516
+ }
29517
+ const deleted = taskEngine.deleteTask(input.task_id);
29518
+ const output = {
29519
+ task_id: input.task_id,
29520
+ deleted,
29521
+ previous_status: task.status,
29522
+ message: deleted ? `Task ${input.task_id} deleted successfully` : `Failed to delete task ${input.task_id}`
29523
+ };
29524
+ logger.info("[delete_task] Task deletion result", {
29525
+ taskId: input.task_id,
29526
+ deleted,
29527
+ previousStatus: task.status
29528
+ });
29529
+ return output;
29530
+ } catch (error) {
29531
+ logger.error("[delete_task] Failed to delete task", {
29532
+ error: error instanceof Error ? error.message : String(error),
29533
+ taskId: input.task_id
29534
+ });
29535
+ throw error;
29536
+ }
29537
+ };
29538
+ }
29539
+ var deleteTaskSchema = {
29540
+ name: "delete_task",
29541
+ description: "Delete a task and its associated data. Cannot delete running tasks unless force=true.",
29542
+ inputSchema: {
29543
+ type: "object",
29544
+ properties: {
29545
+ task_id: {
29546
+ type: "string",
29547
+ description: "Task ID to delete"
29548
+ },
29549
+ force: {
29550
+ type: "boolean",
29551
+ default: false,
29552
+ description: "Force delete even if task is running"
29553
+ }
29554
+ },
29555
+ required: ["task_id"]
29556
+ }
29557
+ };
29558
+
27572
29559
  // src/providers/mcp/pool-manager.ts
27573
29560
  init_esm_shims();
27574
29561
  init_logger();
@@ -27988,7 +29975,7 @@ var ConnectionTimeoutError = class extends McpClientError {
27988
29975
  };
27989
29976
 
27990
29977
  // src/providers/mcp/pool-manager.ts
27991
- var DEFAULT_CONFIG3 = {
29978
+ var DEFAULT_CONFIG6 = {
27992
29979
  maxConnectionsPerProvider: 2,
27993
29980
  idleTimeoutMs: 3e5,
27994
29981
  // 5 minutes
@@ -28009,7 +29996,7 @@ var McpClientPool = class extends EventEmitter {
28009
29996
  drainHandler;
28010
29997
  constructor(config = {}) {
28011
29998
  super();
28012
- this.config = { ...DEFAULT_CONFIG3, ...config };
29999
+ this.config = { ...DEFAULT_CONFIG6, ...config };
28013
30000
  this.startHealthChecks();
28014
30001
  this.startIdleCleanup();
28015
30002
  this.drainHandler = () => this.drain();
@@ -29931,7 +31918,13 @@ var McpServer = class _McpServer {
29931
31918
  },
29932
31919
  required: ["agent", "task"]
29933
31920
  }
29934
- }
31921
+ },
31922
+ // v11.3.5: Task Engine tools
31923
+ createTaskSchema,
31924
+ runTaskSchema,
31925
+ getTaskResultSchema,
31926
+ listTasksSchema,
31927
+ deleteTaskSchema
29935
31928
  ];
29936
31929
  }
29937
31930
  /**
@@ -30106,6 +32099,15 @@ var McpServer = class _McpServer {
30106
32099
  profileLoader: this.profileLoader,
30107
32100
  memoryManager: this.memoryManager
30108
32101
  }));
32102
+ register("create_task", createCreateTaskHandler({
32103
+ getSession: () => this.session
32104
+ }));
32105
+ register("run_task", createRunTaskHandler({
32106
+ getSession: () => this.session
32107
+ }));
32108
+ register("get_task_result", createGetTaskResultHandler());
32109
+ register("list_tasks", createListTasksHandler());
32110
+ register("delete_task", createDeleteTaskHandler());
30109
32111
  logger.info("[MCP Server] Registered tools", {
30110
32112
  count: this.tools.size,
30111
32113
  tools: Array.from(this.tools.keys())
@@ -32408,9 +34410,9 @@ var ProgressIndicator = class {
32408
34410
  // src/cli/commands/memory.ts
32409
34411
  var DEFAULT_DB_PATH = ".automatosx/memory/memory.db";
32410
34412
  function getMemoryManager(dbPath) {
32411
- const path8 = dbPath || DEFAULT_DB_PATH;
34413
+ const path9 = dbPath || DEFAULT_DB_PATH;
32412
34414
  return new LazyMemoryManager({
32413
- dbPath: resolve(path8),
34415
+ dbPath: resolve(path9),
32414
34416
  maxEntries: 1e5,
32415
34417
  autoCleanup: false,
32416
34418
  trackAccess: true
@@ -35414,14 +37416,14 @@ var IterateClassifier = class _IterateClassifier {
35414
37416
  * **Phase 1 (Week 1)**: Skeleton only (no-op)
35415
37417
  * **Phase 2 (Week 2)**: Full implementation with YAML loading and validation
35416
37418
  */
35417
- async loadPatterns(path8) {
35418
- logger.debug("Loading pattern library", { path: path8 });
35419
- if (!existsSync(path8)) {
35420
- logger.warn("Pattern library file not found", { path: path8 });
37419
+ async loadPatterns(path9) {
37420
+ logger.debug("Loading pattern library", { path: path9 });
37421
+ if (!existsSync(path9)) {
37422
+ logger.warn("Pattern library file not found", { path: path9 });
35421
37423
  return;
35422
37424
  }
35423
37425
  try {
35424
- const fileContent = await readFile(path8, "utf-8");
37426
+ const fileContent = await readFile(path9, "utf-8");
35425
37427
  const parsed = load(fileContent);
35426
37428
  if (!parsed || typeof parsed !== "object") {
35427
37429
  throw new Error("Invalid pattern library format: not an object");
@@ -35464,7 +37466,7 @@ var IterateClassifier = class _IterateClassifier {
35464
37466
  });
35465
37467
  } catch (error) {
35466
37468
  logger.error("Failed to load pattern library", {
35467
- path: path8,
37469
+ path: path9,
35468
37470
  error: error.message
35469
37471
  });
35470
37472
  throw error;
@@ -35891,14 +37893,14 @@ var IterateAutoResponder = class {
35891
37893
  * **Phase 1 (Week 1)**: Skeleton only (no-op)
35892
37894
  * **Phase 3 (Week 3)**: Full implementation with YAML loading and validation
35893
37895
  */
35894
- async loadTemplates(path8) {
35895
- logger.debug("Loading template library", { path: path8 });
35896
- if (!existsSync(path8)) {
35897
- logger.warn("Template library file not found", { path: path8 });
37896
+ async loadTemplates(path9) {
37897
+ logger.debug("Loading template library", { path: path9 });
37898
+ if (!existsSync(path9)) {
37899
+ logger.warn("Template library file not found", { path: path9 });
35898
37900
  return;
35899
37901
  }
35900
37902
  try {
35901
- const fileContent = await readFile(path8, "utf-8");
37903
+ const fileContent = await readFile(path9, "utf-8");
35902
37904
  const parsed = load(fileContent);
35903
37905
  if (!parsed || typeof parsed !== "object") {
35904
37906
  throw new Error("Invalid template library format: not an object");
@@ -35940,7 +37942,7 @@ var IterateAutoResponder = class {
35940
37942
  });
35941
37943
  } catch (error) {
35942
37944
  logger.error("Failed to load template library", {
35943
- path: path8,
37945
+ path: path9,
35944
37946
  error: error.message
35945
37947
  });
35946
37948
  throw error;
@@ -40191,10 +42193,10 @@ async function getCurrentVersion() {
40191
42193
  return result.dependencies["@defai.digital/automatosx"]?.version || "unknown";
40192
42194
  } catch (error) {
40193
42195
  const { readFile: readFile24 } = await import('fs/promises');
40194
- const { dirname: dirname16, join: join52 } = await import('path');
42196
+ const { dirname: dirname17, join: join52 } = await import('path');
40195
42197
  const { fileURLToPath: fileURLToPath5 } = await import('url');
40196
42198
  const __filename3 = fileURLToPath5(import.meta.url);
40197
- const __dirname4 = dirname16(__filename3);
42199
+ const __dirname4 = dirname17(__filename3);
40198
42200
  const pkgPath = join52(__dirname4, "../../../package.json");
40199
42201
  const content = await readFile24(pkgPath, "utf-8");
40200
42202
  const pkg = JSON.parse(content);
@@ -42265,8 +44267,8 @@ var ConfigManager = class {
42265
44267
  * @returns User configuration or empty object if not found
42266
44268
  */
42267
44269
  async readUserConfig() {
42268
- const path8 = getUserConfigPath();
42269
- return this.readConfig(path8, "user");
44270
+ const path9 = getUserConfigPath();
44271
+ return this.readConfig(path9, "user");
42270
44272
  }
42271
44273
  /**
42272
44274
  * Read project-level Gemini CLI configuration
@@ -42274,8 +44276,8 @@ var ConfigManager = class {
42274
44276
  * @returns Project configuration or empty object if not found
42275
44277
  */
42276
44278
  async readProjectConfig() {
42277
- const path8 = getProjectConfigPath();
42278
- return this.readConfig(path8, "project");
44279
+ const path9 = getProjectConfigPath();
44280
+ return this.readConfig(path9, "project");
42279
44281
  }
42280
44282
  /**
42281
44283
  * Read configuration from a specific path with caching
@@ -42285,7 +44287,7 @@ var ConfigManager = class {
42285
44287
  * @returns Configuration object
42286
44288
  * @private
42287
44289
  */
42288
- async readConfig(path8, scope) {
44290
+ async readConfig(path9, scope) {
42289
44291
  const cached = this.cache.get(scope);
42290
44292
  if (cached && Date.now() - cached.timestamp < this.ttl) {
42291
44293
  return cached.data;
@@ -42296,13 +44298,13 @@ var ConfigManager = class {
42296
44298
  }
42297
44299
  const readOperation = (async () => {
42298
44300
  try {
42299
- const exists = await fileExists(path8);
44301
+ const exists = await fileExists(path9);
42300
44302
  if (!exists) {
42301
44303
  const emptyConfig = {};
42302
44304
  this.cache.set(scope, { data: emptyConfig, timestamp: Date.now() });
42303
44305
  return emptyConfig;
42304
44306
  }
42305
- const config = await readJsonFile(path8);
44307
+ const config = await readJsonFile(path9);
42306
44308
  this.cache.set(scope, { data: config, timestamp: Date.now() });
42307
44309
  return config;
42308
44310
  } catch (error) {
@@ -42311,8 +44313,8 @@ var ConfigManager = class {
42311
44313
  }
42312
44314
  throw new GeminiCLIError(
42313
44315
  "INVALID_CONFIG" /* INVALID_CONFIG */,
42314
- `Failed to read configuration from ${path8}`,
42315
- { path: path8, originalError: error }
44316
+ `Failed to read configuration from ${path9}`,
44317
+ { path: path9, originalError: error }
42316
44318
  );
42317
44319
  } finally {
42318
44320
  this.pendingReads.delete(scope);
@@ -42982,8 +44984,8 @@ var CommandTranslator = class {
42982
44984
  paths.push(getProjectCommandsPath());
42983
44985
  }
42984
44986
  }
42985
- for (const path8 of paths) {
42986
- const discovered = await this.scanDirectory(path8);
44987
+ for (const path9 of paths) {
44988
+ const discovered = await this.scanDirectory(path9);
42987
44989
  commands.push(...discovered);
42988
44990
  }
42989
44991
  return commands;
@@ -43024,8 +45026,8 @@ var CommandTranslator = class {
43024
45026
  const results = [];
43025
45027
  for (const name of commandNames) {
43026
45028
  try {
43027
- const path8 = await this.importCommand(name, outputDir, options);
43028
- results.push(path8);
45029
+ const path9 = await this.importCommand(name, outputDir, options);
45030
+ results.push(path9);
43029
45031
  } catch (error) {
43030
45032
  if (error instanceof GeminiCLIError) {
43031
45033
  console.error(`Failed to import ${name}: ${error.message}`);
@@ -44357,7 +46359,7 @@ var SpecSchemaValidator = class {
44357
46359
  * Convert Ajv error to ValidationIssue
44358
46360
  */
44359
46361
  convertAjvError(error) {
44360
- const path8 = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
46362
+ const path9 = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
44361
46363
  const keyword = error.keyword;
44362
46364
  let message = error.message || "Validation error";
44363
46365
  let suggestion;
@@ -44408,7 +46410,7 @@ var SpecSchemaValidator = class {
44408
46410
  ruleId: `SCHEMA-${keyword.toUpperCase()}`,
44409
46411
  severity: "error",
44410
46412
  message,
44411
- path: path8 || void 0,
46413
+ path: path9 || void 0,
44412
46414
  suggestion
44413
46415
  };
44414
46416
  }
@@ -47227,10 +49229,10 @@ async function handleReset() {
47227
49229
  `));
47228
49230
  }
47229
49231
  async function handleTrace(workspacePath, argv) {
47230
- const { existsSync: existsSync27, readFileSync: readFileSync9, watchFile } = await import('fs');
49232
+ const { existsSync: existsSync28, readFileSync: readFileSync9, watchFile } = await import('fs');
47231
49233
  const { join: join52 } = await import('path');
47232
49234
  const traceFile = join52(workspacePath, ".automatosx/logs/router.trace.jsonl");
47233
- if (!existsSync27(traceFile)) {
49235
+ if (!existsSync28(traceFile)) {
47234
49236
  console.log(chalk5.yellow("\n\u26A0\uFE0F No trace log found\n"));
47235
49237
  console.log(chalk5.gray(`Expected location: ${traceFile}
47236
49238
  `));
@@ -50138,13 +52140,13 @@ Time since last change: ${timeSinceLastChange}ms`
50138
52140
  init_logger();
50139
52141
  var flagManagerInstances = /* @__PURE__ */ new Map();
50140
52142
  function getFlagManager(workspacePath) {
50141
- const path8 = process.cwd();
50142
- if (!flagManagerInstances.has(path8)) {
50143
- const manager = new FeatureFlagManager(path8);
52143
+ const path9 = process.cwd();
52144
+ if (!flagManagerInstances.has(path9)) {
52145
+ const manager = new FeatureFlagManager(path9);
50144
52146
  initializeFlags(manager);
50145
- flagManagerInstances.set(path8, manager);
52147
+ flagManagerInstances.set(path9, manager);
50146
52148
  }
50147
- return flagManagerInstances.get(path8);
52149
+ return flagManagerInstances.get(path9);
50148
52150
  }
50149
52151
  function initializeFlags(manager) {
50150
52152
  if (!manager.hasStorage()) {
@@ -50465,9 +52467,9 @@ var cliCommand = {
50465
52467
  // src/cli/commands/uninstall.ts
50466
52468
  init_esm_shims();
50467
52469
  init_logger();
50468
- async function fileExists2(path8) {
52470
+ async function fileExists2(path9) {
50469
52471
  try {
50470
- await access$1(path8);
52472
+ await access$1(path9);
50471
52473
  return true;
50472
52474
  } catch {
50473
52475
  return false;
@@ -51459,7 +53461,7 @@ var TodoInstructionProvider = class {
51459
53461
  };
51460
53462
  function computeTodoHash(todos) {
51461
53463
  const stateString = todos.map((t) => `${t.id}:${t.status}:${t.content}`).sort().join("|");
51462
- return crypto2.createHash("sha256").update(stateString).digest("hex").substring(0, 16);
53464
+ return crypto4.createHash("sha256").update(stateString).digest("hex").substring(0, 16);
51463
53465
  }
51464
53466
 
51465
53467
  // src/core/orchestration/memory-instruction-provider.ts