@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/README.md +1 -1
- package/dist/index.js +2195 -193
- package/dist/mcp/index.js +2024 -84
- package/package.json +14 -14
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
|
|
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
|
|
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
|
|
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(
|
|
747
|
+
static notFound(path9) {
|
|
747
748
|
return new _ConfigError(
|
|
748
|
-
`Configuration file not found: ${
|
|
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:
|
|
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,
|
|
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:
|
|
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(
|
|
1008
|
-
return /^[a-zA-Z]:[/\\]/.test(
|
|
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(
|
|
1115
|
-
const normalized = normalizePath(resolvePath(
|
|
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(
|
|
1126
|
-
const boundary = this.checkBoundaries(
|
|
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(
|
|
1133
|
-
const normalized = resolvePath(
|
|
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(
|
|
1152
|
-
const normalized = resolvePath(
|
|
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(
|
|
1159
|
-
const normalized = resolvePath(
|
|
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(
|
|
1179
|
-
const boundary = this.checkBoundaries(
|
|
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:
|
|
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(
|
|
1426
|
-
if (
|
|
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 (
|
|
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(
|
|
1447
|
+
calculateImportance(path9, type, size) {
|
|
1447
1448
|
let score = 0.5;
|
|
1448
|
-
if (
|
|
1449
|
-
if (
|
|
1450
|
-
if (
|
|
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 (
|
|
1453
|
-
if (
|
|
1454
|
-
if (
|
|
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
|
|
1931
|
-
if (
|
|
1932
|
-
logger.debug("Found via which", { cmdBase, path:
|
|
1933
|
-
return { found: true, path:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
4826
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
5713
|
-
const files = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
6049
|
+
await fs6.writeFile(tempFile, content, "utf-8");
|
|
6006
6050
|
try {
|
|
6007
|
-
await
|
|
6051
|
+
await fs6.rename(tempFile, filepath);
|
|
6008
6052
|
} catch (renameError) {
|
|
6009
6053
|
try {
|
|
6010
|
-
await
|
|
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
|
|
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
|
|
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
|
|
6245
|
+
const buildResult = sdk.buildSystemPrompt({
|
|
6202
6246
|
customInstructions: "",
|
|
6203
6247
|
includeMemory: true
|
|
6204
6248
|
});
|
|
6205
|
-
const
|
|
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
|
|
6336
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
7477
|
-
completion:
|
|
7478
|
-
total:
|
|
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
|
|
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(
|
|
9353
|
-
if (!
|
|
9415
|
+
function isValidRelativePath(path9) {
|
|
9416
|
+
if (!path9 || typeof path9 !== "string") {
|
|
9354
9417
|
return false;
|
|
9355
9418
|
}
|
|
9356
|
-
const normalizedPath =
|
|
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
|
-
(
|
|
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(
|
|
10476
|
+
async function loadConfigFile(path9) {
|
|
10414
10477
|
try {
|
|
10415
|
-
const content = await readFile(
|
|
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
|
-
|
|
10482
|
+
path9
|
|
10420
10483
|
);
|
|
10421
10484
|
}
|
|
10422
|
-
const ext = extname(
|
|
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,
|
|
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:
|
|
10510
|
+
{ path: path9, errors: validationErrors }
|
|
10448
10511
|
);
|
|
10449
10512
|
}
|
|
10450
|
-
logger.info("Config loaded successfully", { path: normalizePath(
|
|
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(
|
|
10520
|
+
throw ConfigError.notFound(path9);
|
|
10458
10521
|
}
|
|
10459
10522
|
if (error.code === "EACCES") {
|
|
10460
10523
|
throw new ConfigError(
|
|
10461
|
-
`Permission denied reading config: ${
|
|
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:
|
|
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:
|
|
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
|
|
10492
|
-
return `${
|
|
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(
|
|
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:
|
|
10947
|
+
{ path: path9, errors: validationErrors }
|
|
10885
10948
|
);
|
|
10886
10949
|
}
|
|
10887
|
-
const ext = extname(
|
|
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(
|
|
10962
|
+
await writeFile(path9, content, "utf-8");
|
|
10900
10963
|
configCache.clear();
|
|
10901
|
-
logger.info("Config saved successfully", { path: normalizePath(
|
|
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: ${
|
|
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:
|
|
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:
|
|
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
|
|
11673
|
-
if (
|
|
11735
|
+
const fs9 = await import('fs');
|
|
11736
|
+
if (fs9.existsSync(projectConfig)) {
|
|
11674
11737
|
configPath = projectConfig;
|
|
11675
|
-
} else if (
|
|
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(
|
|
11785
|
+
async function checkExists(path9) {
|
|
11723
11786
|
try {
|
|
11724
|
-
await access$1(
|
|
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(
|
|
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(
|
|
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: ${
|
|
11837
|
+
Config file: ${path9}
|
|
11775
11838
|
`));
|
|
11776
11839
|
}
|
|
11777
|
-
logger.info("Configuration reset", { path:
|
|
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(
|
|
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(
|
|
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: ${
|
|
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,
|
|
11885
|
-
if (!
|
|
11886
|
-
return
|
|
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,
|
|
11889
|
-
const keys =
|
|
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((
|
|
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(
|
|
14375
|
+
async function checkExists2(path9) {
|
|
14313
14376
|
try {
|
|
14314
|
-
await access$1(
|
|
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
|
|
15889
|
+
const path9 = match[2];
|
|
15827
15890
|
endpoints.push({
|
|
15828
15891
|
method,
|
|
15829
|
-
path:
|
|
15830
|
-
group:
|
|
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:
|
|
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 (
|
|
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 (
|
|
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
|
|
18887
|
+
let path9;
|
|
18825
18888
|
{
|
|
18826
18889
|
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
18827
|
-
|
|
18890
|
+
path9 = process.cwd();
|
|
18828
18891
|
} else {
|
|
18829
|
-
|
|
18892
|
+
path9 = await detectProjectRoot();
|
|
18830
18893
|
}
|
|
18831
18894
|
}
|
|
18832
|
-
if (!providerSessionInstances.has(
|
|
18833
|
-
providerSessionInstances.set(
|
|
18895
|
+
if (!providerSessionInstances.has(path9)) {
|
|
18896
|
+
providerSessionInstances.set(path9, new ProviderSessionManager(path9));
|
|
18834
18897
|
}
|
|
18835
|
-
return providerSessionInstances.get(
|
|
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,
|
|
24744
|
+
const visit = (nodeName, path9) => {
|
|
24682
24745
|
if (visiting.has(nodeName)) {
|
|
24683
|
-
throw new Error(`Circular dependency detected: ${[...
|
|
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, [...
|
|
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(
|
|
26432
|
-
if (!
|
|
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:
|
|
26499
|
+
{ path: path9, paramName }
|
|
26437
26500
|
);
|
|
26438
26501
|
}
|
|
26439
26502
|
for (const pattern of DANGEROUS_PATH_PATTERNS) {
|
|
26440
|
-
if (
|
|
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:
|
|
26507
|
+
{ path: path9, paramName, pattern }
|
|
26445
26508
|
);
|
|
26446
26509
|
}
|
|
26447
26510
|
}
|
|
26448
|
-
if (isAbsolute(
|
|
26511
|
+
if (isAbsolute(path9)) {
|
|
26449
26512
|
throw new ValidationError(
|
|
26450
26513
|
`Invalid ${paramName}: absolute paths are not allowed`,
|
|
26451
26514
|
-32602 /* InvalidParams */,
|
|
26452
|
-
{ path:
|
|
26515
|
+
{ path: path9, paramName }
|
|
26453
26516
|
);
|
|
26454
26517
|
}
|
|
26455
26518
|
try {
|
|
26456
|
-
const resolvedPath = resolve(projectRoot,
|
|
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:
|
|
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:
|
|
26532
|
+
{ path: path9, paramName, error: String(error) }
|
|
26470
26533
|
);
|
|
26471
26534
|
}
|
|
26472
|
-
if (
|
|
26535
|
+
if (path9.includes("\0")) {
|
|
26473
26536
|
throw new ValidationError(
|
|
26474
26537
|
`Invalid ${paramName}: path contains null byte`,
|
|
26475
26538
|
-32602 /* InvalidParams */,
|
|
26476
|
-
{ path:
|
|
26539
|
+
{ path: path9, paramName }
|
|
26477
26540
|
);
|
|
26478
26541
|
}
|
|
26479
|
-
if (SUSPICIOUS_PATH_CHARS.test(
|
|
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:
|
|
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:
|
|
27180
|
-
const absolutePath = resolveExportPath(deps.pathResolver,
|
|
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:
|
|
27217
|
-
const absolutePath = resolveImportPath(deps.pathResolver,
|
|
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
|
|
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 = { ...
|
|
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
|
|
34413
|
+
const path9 = dbPath || DEFAULT_DB_PATH;
|
|
32412
34414
|
return new LazyMemoryManager({
|
|
32413
|
-
dbPath: resolve(
|
|
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(
|
|
35418
|
-
logger.debug("Loading pattern library", { path:
|
|
35419
|
-
if (!existsSync(
|
|
35420
|
-
logger.warn("Pattern library file not found", { path:
|
|
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(
|
|
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:
|
|
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(
|
|
35895
|
-
logger.debug("Loading template library", { path:
|
|
35896
|
-
if (!existsSync(
|
|
35897
|
-
logger.warn("Template library file not found", { path:
|
|
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(
|
|
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:
|
|
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:
|
|
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 =
|
|
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
|
|
42269
|
-
return this.readConfig(
|
|
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
|
|
42278
|
-
return this.readConfig(
|
|
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(
|
|
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(
|
|
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(
|
|
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 ${
|
|
42315
|
-
{ path:
|
|
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
|
|
42986
|
-
const discovered = await this.scanDirectory(
|
|
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
|
|
43028
|
-
results.push(
|
|
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
|
|
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:
|
|
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:
|
|
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 (!
|
|
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
|
|
50142
|
-
if (!flagManagerInstances.has(
|
|
50143
|
-
const manager = new FeatureFlagManager(
|
|
52143
|
+
const path9 = process.cwd();
|
|
52144
|
+
if (!flagManagerInstances.has(path9)) {
|
|
52145
|
+
const manager = new FeatureFlagManager(path9);
|
|
50144
52146
|
initializeFlags(manager);
|
|
50145
|
-
flagManagerInstances.set(
|
|
52147
|
+
flagManagerInstances.set(path9, manager);
|
|
50146
52148
|
}
|
|
50147
|
-
return flagManagerInstances.get(
|
|
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(
|
|
52470
|
+
async function fileExists2(path9) {
|
|
50469
52471
|
try {
|
|
50470
|
-
await access$1(
|
|
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
|
|
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
|