@defai.digital/automatosx 11.3.1 → 11.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/README.md +1 -1
- package/dist/index.js +1687 -488
- package/dist/mcp/index.js +424 -167
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
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 fs5 from 'fs/promises';
|
|
6
6
|
import { mkdir, appendFile, access as access$1, readFile, stat, rm, readdir, copyFile, writeFile, rename, unlink, constants as constants$1, realpath as realpath$1 } from 'fs/promises';
|
|
7
|
+
import * as fs3 from 'fs';
|
|
7
8
|
import { access, realpath, existsSync, readFileSync, constants, writeFileSync, mkdirSync, promises, statSync, readdirSync, createWriteStream, unlinkSync } from 'fs';
|
|
8
9
|
import Database2 from 'better-sqlite3';
|
|
9
10
|
import os2, { homedir, cpus } from 'os';
|
|
@@ -17,9 +18,9 @@ import * as readline2 from 'readline';
|
|
|
17
18
|
import readline2__default, { createInterface } from 'readline';
|
|
18
19
|
import { Mutex } from 'async-mutex';
|
|
19
20
|
import { promisify } from 'util';
|
|
21
|
+
import crypto2, { randomUUID, createHash } from 'crypto';
|
|
20
22
|
import yargs from 'yargs';
|
|
21
23
|
import { hideBin } from 'yargs/helpers';
|
|
22
|
-
import crypto2, { randomUUID, createHash } from 'crypto';
|
|
23
24
|
import * as yaml4 from 'js-yaml';
|
|
24
25
|
import yaml4__default, { load, dump } from 'js-yaml';
|
|
25
26
|
import Table from 'cli-table3';
|
|
@@ -32,9 +33,7 @@ import yaml from 'yaml';
|
|
|
32
33
|
import { parse } from '@iarna/toml';
|
|
33
34
|
|
|
34
35
|
var __defProp = Object.defineProperty;
|
|
35
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
36
36
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
37
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
38
37
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
39
38
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
40
39
|
}) : x)(function(x) {
|
|
@@ -48,15 +47,6 @@ var __export = (target, all) => {
|
|
|
48
47
|
for (var name in all)
|
|
49
48
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
50
49
|
};
|
|
51
|
-
var __copyProps = (to, from, except, desc) => {
|
|
52
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
53
|
-
for (let key of __getOwnPropNames(from))
|
|
54
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
55
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
56
|
-
}
|
|
57
|
-
return to;
|
|
58
|
-
};
|
|
59
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
60
50
|
var init_esm_shims = __esm({
|
|
61
51
|
"node_modules/tsup/assets/esm_shims.js"() {
|
|
62
52
|
}
|
|
@@ -753,16 +743,16 @@ var init_errors = __esm({
|
|
|
753
743
|
constructor(message, code = "E1001" /* CONFIG_INVALID */, suggestions = [], context) {
|
|
754
744
|
super(message, code, suggestions, context);
|
|
755
745
|
}
|
|
756
|
-
static notFound(
|
|
746
|
+
static notFound(path8) {
|
|
757
747
|
return new _ConfigError(
|
|
758
|
-
`Configuration file not found: ${
|
|
748
|
+
`Configuration file not found: ${path8}`,
|
|
759
749
|
"E1000" /* CONFIG_NOT_FOUND */,
|
|
760
750
|
[
|
|
761
751
|
'Run "automatosx setup" to create a new configuration',
|
|
762
752
|
"Specify a custom config path with --config option",
|
|
763
753
|
"Check that you are in a valid AutomatosX project directory"
|
|
764
754
|
],
|
|
765
|
-
{ path:
|
|
755
|
+
{ path: path8 }
|
|
766
756
|
);
|
|
767
757
|
}
|
|
768
758
|
static invalid(reason, context) {
|
|
@@ -777,7 +767,7 @@ var init_errors = __esm({
|
|
|
777
767
|
context
|
|
778
768
|
);
|
|
779
769
|
}
|
|
780
|
-
static parseError(error,
|
|
770
|
+
static parseError(error, path8) {
|
|
781
771
|
return new _ConfigError(
|
|
782
772
|
`Failed to parse configuration: ${error.message}`,
|
|
783
773
|
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
@@ -786,7 +776,7 @@ var init_errors = __esm({
|
|
|
786
776
|
"Use a JSON validator to find syntax errors",
|
|
787
777
|
'Reset to default with "automatosx setup --force"'
|
|
788
778
|
],
|
|
789
|
-
{ path:
|
|
779
|
+
{ path: path8, originalError: error.message }
|
|
790
780
|
);
|
|
791
781
|
}
|
|
792
782
|
};
|
|
@@ -1014,8 +1004,8 @@ __export(path_resolver_exports, {
|
|
|
1014
1004
|
PathResolver: () => PathResolver,
|
|
1015
1005
|
detectProjectRoot: () => detectProjectRoot
|
|
1016
1006
|
});
|
|
1017
|
-
function isWindowsPath(
|
|
1018
|
-
return /^[a-zA-Z]:[/\\]/.test(
|
|
1007
|
+
function isWindowsPath(path8) {
|
|
1008
|
+
return /^[a-zA-Z]:[/\\]/.test(path8);
|
|
1019
1009
|
}
|
|
1020
1010
|
async function detectProjectRoot(startDir = process.cwd()) {
|
|
1021
1011
|
if (process.env.AUTOMATOSX_PROJECT_ROOT) {
|
|
@@ -1121,8 +1111,8 @@ var init_path_resolver = __esm({
|
|
|
1121
1111
|
/**
|
|
1122
1112
|
* Validate path is within allowed base directory
|
|
1123
1113
|
*/
|
|
1124
|
-
validatePath(
|
|
1125
|
-
const normalized = normalizePath(resolvePath(
|
|
1114
|
+
validatePath(path8, baseDir) {
|
|
1115
|
+
const normalized = normalizePath(resolvePath(path8));
|
|
1126
1116
|
const base = normalizePath(resolvePath(baseDir));
|
|
1127
1117
|
const separator = "/";
|
|
1128
1118
|
const pathWithSep = normalized + separator;
|
|
@@ -1132,15 +1122,15 @@ var init_path_resolver = __esm({
|
|
|
1132
1122
|
/**
|
|
1133
1123
|
* Check if path is within allowed boundaries
|
|
1134
1124
|
*/
|
|
1135
|
-
isPathAllowed(
|
|
1136
|
-
const boundary = this.checkBoundaries(
|
|
1125
|
+
isPathAllowed(path8) {
|
|
1126
|
+
const boundary = this.checkBoundaries(path8);
|
|
1137
1127
|
return boundary === "agent_workspace" || boundary === "user_project";
|
|
1138
1128
|
}
|
|
1139
1129
|
/**
|
|
1140
1130
|
* Check which boundary a path belongs to
|
|
1141
1131
|
*/
|
|
1142
|
-
checkBoundaries(
|
|
1143
|
-
const normalized = resolvePath(
|
|
1132
|
+
checkBoundaries(path8) {
|
|
1133
|
+
const normalized = resolvePath(path8);
|
|
1144
1134
|
if (this.validatePath(normalized, this.config.agentWorkspace)) {
|
|
1145
1135
|
return "agent_workspace";
|
|
1146
1136
|
}
|
|
@@ -1158,15 +1148,15 @@ var init_path_resolver = __esm({
|
|
|
1158
1148
|
/**
|
|
1159
1149
|
* Get relative path from project root
|
|
1160
1150
|
*/
|
|
1161
|
-
getRelativeToProject(
|
|
1162
|
-
const normalized = resolvePath(
|
|
1151
|
+
getRelativeToProject(path8) {
|
|
1152
|
+
const normalized = resolvePath(path8);
|
|
1163
1153
|
return normalizePath(getRelativePath(this.config.projectDir, normalized));
|
|
1164
1154
|
}
|
|
1165
1155
|
/**
|
|
1166
1156
|
* Get relative path from working directory
|
|
1167
1157
|
*/
|
|
1168
|
-
getRelativeToWorking(
|
|
1169
|
-
const normalized = resolvePath(
|
|
1158
|
+
getRelativeToWorking(path8) {
|
|
1159
|
+
const normalized = resolvePath(path8);
|
|
1170
1160
|
return normalizePath(getRelativePath(this.config.workingDir, normalized));
|
|
1171
1161
|
}
|
|
1172
1162
|
/**
|
|
@@ -1185,11 +1175,11 @@ var init_path_resolver = __esm({
|
|
|
1185
1175
|
* Validate path is within project boundaries
|
|
1186
1176
|
* @throws PathError if outside project
|
|
1187
1177
|
*/
|
|
1188
|
-
validateInProject(
|
|
1189
|
-
const boundary = this.checkBoundaries(
|
|
1178
|
+
validateInProject(path8) {
|
|
1179
|
+
const boundary = this.checkBoundaries(path8);
|
|
1190
1180
|
if (boundary === "outside_boundaries" || boundary === "system_restricted") {
|
|
1191
1181
|
throw new PathError("Path outside project directory", {
|
|
1192
|
-
path:
|
|
1182
|
+
path: path8,
|
|
1193
1183
|
projectDir: this.config.projectDir,
|
|
1194
1184
|
boundary
|
|
1195
1185
|
});
|
|
@@ -1432,11 +1422,11 @@ var init_workspace_indexer = __esm({
|
|
|
1432
1422
|
/**
|
|
1433
1423
|
* Detect file type and language
|
|
1434
1424
|
*/
|
|
1435
|
-
detectTypeAndLanguage(
|
|
1436
|
-
if (
|
|
1425
|
+
detectTypeAndLanguage(path8, ext) {
|
|
1426
|
+
if (path8.match(/package\.json|tsconfig\.json|\.config\.(js|ts|json|yaml|yml)|\.eslintrc|\.prettierrc/)) {
|
|
1437
1427
|
return { type: "config", language: "json" };
|
|
1438
1428
|
}
|
|
1439
|
-
if (
|
|
1429
|
+
if (path8.match(/\.(test|spec)\.(ts|js|tsx|jsx)$/) || path8.includes("__tests__")) {
|
|
1440
1430
|
const lang = LANGUAGE_MAP[ext];
|
|
1441
1431
|
return { type: "test", language: lang };
|
|
1442
1432
|
}
|
|
@@ -1453,15 +1443,15 @@ var init_workspace_indexer = __esm({
|
|
|
1453
1443
|
*
|
|
1454
1444
|
* Higher score = more likely to be relevant
|
|
1455
1445
|
*/
|
|
1456
|
-
calculateImportance(
|
|
1446
|
+
calculateImportance(path8, type, size) {
|
|
1457
1447
|
let score = 0.5;
|
|
1458
|
-
if (
|
|
1459
|
-
if (
|
|
1460
|
-
if (
|
|
1448
|
+
if (path8.match(/^(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 1;
|
|
1449
|
+
if (path8.match(/^src\/(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 0.95;
|
|
1450
|
+
if (path8.match(/^src\/cli\/(index|main)\.(ts|js)$/)) score = 0.9;
|
|
1461
1451
|
if (type === "config") score = 0.85;
|
|
1462
|
-
if (
|
|
1463
|
-
if (
|
|
1464
|
-
if (
|
|
1452
|
+
if (path8.includes("/core/")) score += 0.15;
|
|
1453
|
+
if (path8.includes("/types/")) score += 0.1;
|
|
1454
|
+
if (path8.includes("/utils/")) score += 0.05;
|
|
1465
1455
|
if (type === "test") score = Math.max(0.3, score - 0.3);
|
|
1466
1456
|
if (type === "doc") score = Math.max(0.4, score - 0.2);
|
|
1467
1457
|
if (size > 1e4) score += 0.05;
|
|
@@ -1937,10 +1927,10 @@ function findOnPathUnix(cmdBase) {
|
|
|
1937
1927
|
try {
|
|
1938
1928
|
const which = spawnSync("which", [cmdBase], { timeout: 3e3 });
|
|
1939
1929
|
if (which.status === 0) {
|
|
1940
|
-
const
|
|
1941
|
-
if (
|
|
1942
|
-
logger.debug("Found via which", { cmdBase, path:
|
|
1943
|
-
return { found: true, path:
|
|
1930
|
+
const path8 = which.stdout.toString().trim();
|
|
1931
|
+
if (path8) {
|
|
1932
|
+
logger.debug("Found via which", { cmdBase, path: path8 });
|
|
1933
|
+
return { found: true, path: path8 };
|
|
1944
1934
|
}
|
|
1945
1935
|
}
|
|
1946
1936
|
} catch (error) {
|
|
@@ -1981,8 +1971,8 @@ var init_provider_schemas = __esm({
|
|
|
1981
1971
|
completion: z.number().int().nonnegative(),
|
|
1982
1972
|
total: z.number().int().nonnegative()
|
|
1983
1973
|
}).refine(
|
|
1984
|
-
(data) => data.total
|
|
1985
|
-
"Total tokens must equal prompt + completion"
|
|
1974
|
+
(data) => data.total >= data.prompt + data.completion,
|
|
1975
|
+
"Total tokens must be greater than or equal to prompt + completion"
|
|
1986
1976
|
).describe("Token usage information");
|
|
1987
1977
|
finishReasonSchema = z.enum([
|
|
1988
1978
|
"stop",
|
|
@@ -2239,15 +2229,6 @@ var init_streaming_progress_parser = __esm({
|
|
|
2239
2229
|
});
|
|
2240
2230
|
|
|
2241
2231
|
// src/providers/error-patterns.ts
|
|
2242
|
-
var error_patterns_exports = {};
|
|
2243
|
-
__export(error_patterns_exports, {
|
|
2244
|
-
GENERIC_ERROR_PATTERNS: () => GENERIC_ERROR_PATTERNS,
|
|
2245
|
-
PROVIDER_ERROR_PATTERNS: () => PROVIDER_ERROR_PATTERNS,
|
|
2246
|
-
getProviderErrorPatterns: () => getProviderErrorPatterns,
|
|
2247
|
-
isLimitError: () => isLimitError,
|
|
2248
|
-
isQuotaError: () => isQuotaError,
|
|
2249
|
-
isRateLimitError: () => isRateLimitError
|
|
2250
|
-
});
|
|
2251
2232
|
function isQuotaError(error, providerName) {
|
|
2252
2233
|
const patterns = PROVIDER_ERROR_PATTERNS[providerName] || GENERIC_ERROR_PATTERNS;
|
|
2253
2234
|
const message = (error?.message || "").toLowerCase();
|
|
@@ -2289,9 +2270,6 @@ function isRateLimitError(error, providerName) {
|
|
|
2289
2270
|
function isLimitError(error, providerName) {
|
|
2290
2271
|
return isQuotaError(error, providerName) || isRateLimitError(error, providerName);
|
|
2291
2272
|
}
|
|
2292
|
-
function getProviderErrorPatterns(providerName) {
|
|
2293
|
-
return PROVIDER_ERROR_PATTERNS[providerName] || GENERIC_ERROR_PATTERNS;
|
|
2294
|
-
}
|
|
2295
2273
|
var PROVIDER_ERROR_PATTERNS, GENERIC_ERROR_PATTERNS;
|
|
2296
2274
|
var init_error_patterns = __esm({
|
|
2297
2275
|
"src/providers/error-patterns.ts"() {
|
|
@@ -2480,6 +2458,62 @@ var init_error_patterns = __esm({
|
|
|
2480
2458
|
"quota_exceeded",
|
|
2481
2459
|
"billing_hard_limit_reached"
|
|
2482
2460
|
]
|
|
2461
|
+
},
|
|
2462
|
+
/**
|
|
2463
|
+
* ax-cli Provider Error Patterns (v9.2.0)
|
|
2464
|
+
*
|
|
2465
|
+
* ax-cli is a multi-model provider supporting GLM, xAI, OpenAI, Anthropic, Ollama, etc.
|
|
2466
|
+
* BUG FIX: Added missing patterns - previously fell back to generic patterns which
|
|
2467
|
+
* may miss provider-specific errors from the underlying model providers.
|
|
2468
|
+
*
|
|
2469
|
+
* Combines patterns from all supported backends since ax-cli proxies to them.
|
|
2470
|
+
*/
|
|
2471
|
+
"ax-cli": {
|
|
2472
|
+
quota: [
|
|
2473
|
+
// GLM patterns
|
|
2474
|
+
"quota exceeded",
|
|
2475
|
+
"quota limit reached",
|
|
2476
|
+
"insufficient quota",
|
|
2477
|
+
// OpenAI patterns (via ax-cli)
|
|
2478
|
+
"insufficient_quota",
|
|
2479
|
+
"quota_exceeded",
|
|
2480
|
+
"billing hard limit reached",
|
|
2481
|
+
"usage limit exceeded",
|
|
2482
|
+
"monthly quota exceeded",
|
|
2483
|
+
"credit limit reached",
|
|
2484
|
+
// Anthropic patterns (via ax-cli)
|
|
2485
|
+
"credit limit reached",
|
|
2486
|
+
// Generic patterns
|
|
2487
|
+
"daily quota exceeded",
|
|
2488
|
+
"api quota exceeded"
|
|
2489
|
+
],
|
|
2490
|
+
rateLimit: [
|
|
2491
|
+
// Common patterns
|
|
2492
|
+
"rate_limit_exceeded",
|
|
2493
|
+
"rate limit exceeded",
|
|
2494
|
+
"too_many_requests",
|
|
2495
|
+
"too many requests",
|
|
2496
|
+
"requests per minute exceeded",
|
|
2497
|
+
"tokens per minute exceeded",
|
|
2498
|
+
"rate limit reached",
|
|
2499
|
+
// Anthropic patterns (via ax-cli)
|
|
2500
|
+
"rate_limit_error",
|
|
2501
|
+
"overloaded_error",
|
|
2502
|
+
"overloaded",
|
|
2503
|
+
// xAI/Grok patterns
|
|
2504
|
+
"throttled",
|
|
2505
|
+
"request throttled"
|
|
2506
|
+
],
|
|
2507
|
+
statusCodes: [429, 529],
|
|
2508
|
+
errorCodes: [
|
|
2509
|
+
"insufficient_quota",
|
|
2510
|
+
"rate_limit_exceeded",
|
|
2511
|
+
"quota_exceeded",
|
|
2512
|
+
"rate_limit_error",
|
|
2513
|
+
"overloaded_error",
|
|
2514
|
+
"RATE_LIMIT_EXCEEDED",
|
|
2515
|
+
"QUOTA_EXCEEDED"
|
|
2516
|
+
]
|
|
2483
2517
|
}
|
|
2484
2518
|
};
|
|
2485
2519
|
GENERIC_ERROR_PATTERNS = {
|
|
@@ -2502,6 +2536,105 @@ var init_error_patterns = __esm({
|
|
|
2502
2536
|
};
|
|
2503
2537
|
}
|
|
2504
2538
|
});
|
|
2539
|
+
|
|
2540
|
+
// src/providers/retry-errors.ts
|
|
2541
|
+
function containsErrorPattern(message, patterns) {
|
|
2542
|
+
const lowerMessage = message.toLowerCase();
|
|
2543
|
+
return patterns.some((pattern) => lowerMessage.includes(pattern.toLowerCase()));
|
|
2544
|
+
}
|
|
2545
|
+
function getRetryableErrors(provider) {
|
|
2546
|
+
const baseErrors = [
|
|
2547
|
+
...COMMON_NETWORK_ERRORS,
|
|
2548
|
+
...COMMON_RATE_LIMIT_ERRORS,
|
|
2549
|
+
...COMMON_SERVER_ERRORS
|
|
2550
|
+
];
|
|
2551
|
+
switch (provider) {
|
|
2552
|
+
case "claude":
|
|
2553
|
+
return [...baseErrors, ...CLAUDE_RETRYABLE_ERRORS];
|
|
2554
|
+
case "gemini":
|
|
2555
|
+
return [...baseErrors, ...GEMINI_RETRYABLE_ERRORS];
|
|
2556
|
+
case "openai":
|
|
2557
|
+
return [...baseErrors, ...OPENAI_RETRYABLE_ERRORS];
|
|
2558
|
+
case "ax-cli":
|
|
2559
|
+
return [...baseErrors, ...AX_CLI_RETRYABLE_ERRORS];
|
|
2560
|
+
case "codex":
|
|
2561
|
+
return [...baseErrors, ...CODEX_RETRYABLE_ERRORS];
|
|
2562
|
+
case "base":
|
|
2563
|
+
default:
|
|
2564
|
+
return baseErrors;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
function shouldRetryError(error, provider) {
|
|
2568
|
+
const message = error.message;
|
|
2569
|
+
if (containsErrorPattern(message, NON_RETRYABLE_ERRORS)) {
|
|
2570
|
+
return false;
|
|
2571
|
+
}
|
|
2572
|
+
const retryableErrors = getRetryableErrors(provider);
|
|
2573
|
+
return containsErrorPattern(message, retryableErrors);
|
|
2574
|
+
}
|
|
2575
|
+
var COMMON_NETWORK_ERRORS, COMMON_RATE_LIMIT_ERRORS, COMMON_SERVER_ERRORS, CLAUDE_RETRYABLE_ERRORS, GEMINI_RETRYABLE_ERRORS, OPENAI_RETRYABLE_ERRORS, AX_CLI_RETRYABLE_ERRORS, CODEX_RETRYABLE_ERRORS, NON_RETRYABLE_ERRORS;
|
|
2576
|
+
var init_retry_errors = __esm({
|
|
2577
|
+
"src/providers/retry-errors.ts"() {
|
|
2578
|
+
init_esm_shims();
|
|
2579
|
+
COMMON_NETWORK_ERRORS = [
|
|
2580
|
+
"ECONNRESET",
|
|
2581
|
+
"ETIMEDOUT",
|
|
2582
|
+
"ENOTFOUND",
|
|
2583
|
+
"ECONNREFUSED",
|
|
2584
|
+
"connection_error",
|
|
2585
|
+
"network connection error",
|
|
2586
|
+
"timeout"
|
|
2587
|
+
];
|
|
2588
|
+
COMMON_RATE_LIMIT_ERRORS = [
|
|
2589
|
+
"rate_limit",
|
|
2590
|
+
"rate_limit_error",
|
|
2591
|
+
"too many requests"
|
|
2592
|
+
];
|
|
2593
|
+
COMMON_SERVER_ERRORS = [
|
|
2594
|
+
"internal_server_error",
|
|
2595
|
+
"server_error",
|
|
2596
|
+
"service_unavailable",
|
|
2597
|
+
"unavailable"
|
|
2598
|
+
];
|
|
2599
|
+
CLAUDE_RETRYABLE_ERRORS = [
|
|
2600
|
+
"overloaded_error",
|
|
2601
|
+
"internal_server_error"
|
|
2602
|
+
];
|
|
2603
|
+
GEMINI_RETRYABLE_ERRORS = [
|
|
2604
|
+
"resource_exhausted",
|
|
2605
|
+
"deadline_exceeded",
|
|
2606
|
+
"internal"
|
|
2607
|
+
];
|
|
2608
|
+
OPENAI_RETRYABLE_ERRORS = [
|
|
2609
|
+
"internal_error"
|
|
2610
|
+
];
|
|
2611
|
+
AX_CLI_RETRYABLE_ERRORS = [
|
|
2612
|
+
// Inherited from Anthropic (via ax-cli)
|
|
2613
|
+
"overloaded_error",
|
|
2614
|
+
// Inherited from OpenAI (via ax-cli)
|
|
2615
|
+
"internal_error",
|
|
2616
|
+
// ax-cli specific
|
|
2617
|
+
"agent_error",
|
|
2618
|
+
"tool_execution_error",
|
|
2619
|
+
// xAI/Grok specific
|
|
2620
|
+
"service_overloaded"
|
|
2621
|
+
];
|
|
2622
|
+
CODEX_RETRYABLE_ERRORS = [
|
|
2623
|
+
...OPENAI_RETRYABLE_ERRORS,
|
|
2624
|
+
"sandbox_error"
|
|
2625
|
+
// Codex-specific sandbox issues are often transient
|
|
2626
|
+
];
|
|
2627
|
+
NON_RETRYABLE_ERRORS = [
|
|
2628
|
+
"authentication",
|
|
2629
|
+
"unauthorized",
|
|
2630
|
+
"api key",
|
|
2631
|
+
"not found",
|
|
2632
|
+
"permission denied",
|
|
2633
|
+
"invalid_api_key",
|
|
2634
|
+
"invalid_request"
|
|
2635
|
+
];
|
|
2636
|
+
}
|
|
2637
|
+
});
|
|
2505
2638
|
var BaseProvider;
|
|
2506
2639
|
var init_base_provider = __esm({
|
|
2507
2640
|
"src/providers/base-provider.ts"() {
|
|
@@ -2512,6 +2645,8 @@ var init_base_provider = __esm({
|
|
|
2512
2645
|
init_provider_schemas();
|
|
2513
2646
|
init_streaming_progress_parser();
|
|
2514
2647
|
init_verbosity_manager();
|
|
2648
|
+
init_error_patterns();
|
|
2649
|
+
init_retry_errors();
|
|
2515
2650
|
BaseProvider = class _BaseProvider {
|
|
2516
2651
|
/**
|
|
2517
2652
|
* Whitelist of allowed provider names for security
|
|
@@ -2544,6 +2679,10 @@ var init_base_provider = __esm({
|
|
|
2544
2679
|
NO_UPDATE_NOTIFIER: "1",
|
|
2545
2680
|
DEBIAN_FRONTEND: "noninteractive"
|
|
2546
2681
|
};
|
|
2682
|
+
/** Default CLI execution timeout in milliseconds */
|
|
2683
|
+
static DEFAULT_TIMEOUT_MS = 12e4;
|
|
2684
|
+
/** Time to wait after SIGTERM before escalating to SIGKILL */
|
|
2685
|
+
static SIGKILL_ESCALATION_MS = 5e3;
|
|
2547
2686
|
config;
|
|
2548
2687
|
logger = logger;
|
|
2549
2688
|
health;
|
|
@@ -2561,6 +2700,8 @@ var init_base_provider = __esm({
|
|
|
2561
2700
|
latencyMs: 0,
|
|
2562
2701
|
errorRate: 0,
|
|
2563
2702
|
consecutiveFailures: 0,
|
|
2703
|
+
consecutiveSuccesses: 0,
|
|
2704
|
+
// BUG FIX: Track consecutive successes
|
|
2564
2705
|
lastCheck: Date.now()
|
|
2565
2706
|
};
|
|
2566
2707
|
}
|
|
@@ -2601,7 +2742,7 @@ var init_base_provider = __esm({
|
|
|
2601
2742
|
useStdin,
|
|
2602
2743
|
streaming: process.env.AUTOMATOSX_SHOW_PROVIDER_OUTPUT === "true"
|
|
2603
2744
|
});
|
|
2604
|
-
const result = useStdin ? await this.executeWithStdin(cliCommand2,
|
|
2745
|
+
const result = useStdin ? await this.executeWithStdin(cliCommand2, cliArgs, prompt) : await this.executeWithSpawn(fullCommand, cliCommand2);
|
|
2605
2746
|
if (!result.stdout) {
|
|
2606
2747
|
throw new Error(`${cliCommand2} CLI returned empty output. stderr: ${result.stderr || "none"}`);
|
|
2607
2748
|
}
|
|
@@ -2623,8 +2764,8 @@ var init_base_provider = __esm({
|
|
|
2623
2764
|
/**
|
|
2624
2765
|
* Execute command using spawn() with streaming support (v8.4.18)
|
|
2625
2766
|
*
|
|
2626
|
-
*
|
|
2627
|
-
*
|
|
2767
|
+
* Supports real-time line-by-line output streaming with proper timeout
|
|
2768
|
+
* and cleanup behaviors (SIGTERM → SIGKILL escalation pattern).
|
|
2628
2769
|
*
|
|
2629
2770
|
* @param command - Full command string to execute
|
|
2630
2771
|
* @param cliCommand - CLI command name (for logging)
|
|
@@ -2635,7 +2776,7 @@ var init_base_provider = __esm({
|
|
|
2635
2776
|
const child = spawn(command, [], {
|
|
2636
2777
|
shell: true,
|
|
2637
2778
|
// Auto-detects: cmd.exe on Windows, /bin/sh on Unix
|
|
2638
|
-
timeout: this.config.timeout ||
|
|
2779
|
+
timeout: this.config.timeout || _BaseProvider.DEFAULT_TIMEOUT_MS,
|
|
2639
2780
|
env: { ...process.env, ..._BaseProvider.NON_INTERACTIVE_ENV }
|
|
2640
2781
|
});
|
|
2641
2782
|
let stdout = "";
|
|
@@ -2746,7 +2887,7 @@ var init_base_provider = __esm({
|
|
|
2746
2887
|
});
|
|
2747
2888
|
reject(new Error(`Failed to spawn ${cliCommand2} CLI: ${error.message}`));
|
|
2748
2889
|
});
|
|
2749
|
-
const timeout = this.config.timeout ||
|
|
2890
|
+
const timeout = this.config.timeout || _BaseProvider.DEFAULT_TIMEOUT_MS;
|
|
2750
2891
|
timeoutId = setTimeout(() => {
|
|
2751
2892
|
if (child.pid && !child.killed) {
|
|
2752
2893
|
logger.warn("Killing child process due to timeout", {
|
|
@@ -2760,7 +2901,7 @@ var init_base_provider = __esm({
|
|
|
2760
2901
|
logger.warn("Force killing child process", { pid: child.pid });
|
|
2761
2902
|
child.kill("SIGKILL");
|
|
2762
2903
|
}
|
|
2763
|
-
},
|
|
2904
|
+
}, _BaseProvider.SIGKILL_ESCALATION_MS);
|
|
2764
2905
|
}
|
|
2765
2906
|
}, timeout);
|
|
2766
2907
|
});
|
|
@@ -2778,14 +2919,13 @@ var init_base_provider = __esm({
|
|
|
2778
2919
|
*/
|
|
2779
2920
|
async executeWithStdin(cliCommand2, cliArgs, prompt) {
|
|
2780
2921
|
return new Promise((resolve13, reject) => {
|
|
2781
|
-
const commandArgs = cliArgs ? cliArgs.split(" ").filter(Boolean) : [];
|
|
2782
2922
|
logger.debug(`Executing ${cliCommand2} CLI with stdin`, {
|
|
2783
2923
|
command: cliCommand2,
|
|
2784
|
-
args:
|
|
2924
|
+
args: cliArgs,
|
|
2785
2925
|
promptLength: prompt.length
|
|
2786
2926
|
});
|
|
2787
|
-
const child = spawn(cliCommand2,
|
|
2788
|
-
timeout: this.config.timeout ||
|
|
2927
|
+
const child = spawn(cliCommand2, cliArgs, {
|
|
2928
|
+
timeout: this.config.timeout || _BaseProvider.DEFAULT_TIMEOUT_MS,
|
|
2789
2929
|
env: { ...process.env, ..._BaseProvider.NON_INTERACTIVE_ENV }
|
|
2790
2930
|
});
|
|
2791
2931
|
let stdout = "";
|
|
@@ -2808,11 +2948,20 @@ var init_base_provider = __esm({
|
|
|
2808
2948
|
child.stdin.write(prompt);
|
|
2809
2949
|
child.stdin.end();
|
|
2810
2950
|
} catch (error) {
|
|
2951
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2811
2952
|
logger.error("Failed to write to stdin", {
|
|
2812
2953
|
command: cliCommand2,
|
|
2813
|
-
error:
|
|
2954
|
+
error: errorMessage
|
|
2814
2955
|
});
|
|
2956
|
+
child.kill("SIGTERM");
|
|
2957
|
+
reject(new Error(`Failed to write prompt to ${cliCommand2} stdin: ${errorMessage}`));
|
|
2958
|
+
return;
|
|
2815
2959
|
}
|
|
2960
|
+
} else {
|
|
2961
|
+
logger.error("stdin not available for child process", { command: cliCommand2 });
|
|
2962
|
+
child.kill("SIGTERM");
|
|
2963
|
+
reject(new Error(`${cliCommand2} stdin not available - cannot send prompt`));
|
|
2964
|
+
return;
|
|
2816
2965
|
}
|
|
2817
2966
|
if (child.stdout) {
|
|
2818
2967
|
readlineInterface = readline2__default.createInterface({
|
|
@@ -2921,7 +3070,7 @@ var init_base_provider = __esm({
|
|
|
2921
3070
|
});
|
|
2922
3071
|
reject(new Error(`Failed to spawn ${cliCommand2} CLI: ${error.message}`));
|
|
2923
3072
|
});
|
|
2924
|
-
const timeout = this.config.timeout ||
|
|
3073
|
+
const timeout = this.config.timeout || _BaseProvider.DEFAULT_TIMEOUT_MS;
|
|
2925
3074
|
timeoutId = setTimeout(() => {
|
|
2926
3075
|
if (child.pid && !child.killed) {
|
|
2927
3076
|
logger.warn("Killing child process due to timeout", {
|
|
@@ -2935,7 +3084,7 @@ var init_base_provider = __esm({
|
|
|
2935
3084
|
logger.warn("Force killing child process", { pid: child.pid });
|
|
2936
3085
|
child.kill("SIGKILL");
|
|
2937
3086
|
}
|
|
2938
|
-
},
|
|
3087
|
+
}, _BaseProvider.SIGKILL_ESCALATION_MS);
|
|
2939
3088
|
}
|
|
2940
3089
|
}, timeout);
|
|
2941
3090
|
});
|
|
@@ -3001,10 +3150,8 @@ var init_base_provider = __esm({
|
|
|
3001
3150
|
${request.prompt}`;
|
|
3002
3151
|
}
|
|
3003
3152
|
if (process.env.AUTOMATOSX_DEBUG_PROMPT === "true") {
|
|
3004
|
-
const
|
|
3005
|
-
|
|
3006
|
-
const debugPath = path7.join(process.cwd(), "automatosx/tmp/debug-prompt.txt");
|
|
3007
|
-
fs7.writeFileSync(debugPath, `=== FULL PROMPT SENT TO ${this.getCLICommand()} ===
|
|
3153
|
+
const debugPath = path4.join(process.cwd(), "automatosx/tmp/debug-prompt.txt");
|
|
3154
|
+
fs3.writeFileSync(debugPath, `=== FULL PROMPT SENT TO ${this.getCLICommand()} ===
|
|
3008
3155
|
|
|
3009
3156
|
${fullPrompt}
|
|
3010
3157
|
|
|
@@ -3015,6 +3162,7 @@ ${fullPrompt}
|
|
|
3015
3162
|
const result = await this.executeCLI(fullPrompt);
|
|
3016
3163
|
const latencyMs = Date.now() - startTime;
|
|
3017
3164
|
this.health.consecutiveFailures = 0;
|
|
3165
|
+
this.health.consecutiveSuccesses++;
|
|
3018
3166
|
this.health.available = true;
|
|
3019
3167
|
this.health.errorRate = 0;
|
|
3020
3168
|
this.health.latencyMs = latencyMs;
|
|
@@ -3047,6 +3195,7 @@ ${fullPrompt}
|
|
|
3047
3195
|
return response;
|
|
3048
3196
|
} catch (error) {
|
|
3049
3197
|
this.health.consecutiveFailures++;
|
|
3198
|
+
this.health.consecutiveSuccesses = 0;
|
|
3050
3199
|
this.health.available = false;
|
|
3051
3200
|
this.health.errorRate = 1;
|
|
3052
3201
|
this.health.latencyMs = Date.now() - startTime;
|
|
@@ -3060,19 +3209,23 @@ ${fullPrompt}
|
|
|
3060
3209
|
async isAvailable() {
|
|
3061
3210
|
try {
|
|
3062
3211
|
const available = await this.checkCLIAvailable();
|
|
3063
|
-
this.health =
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
}
|
|
3212
|
+
this.health.available = available;
|
|
3213
|
+
this.health.errorRate = available ? 0 : 1;
|
|
3214
|
+
this.health.lastCheck = Date.now();
|
|
3215
|
+
if (available) {
|
|
3216
|
+
this.health.consecutiveFailures = 0;
|
|
3217
|
+
this.health.consecutiveSuccesses++;
|
|
3218
|
+
} else {
|
|
3219
|
+
this.health.consecutiveFailures++;
|
|
3220
|
+
this.health.consecutiveSuccesses = 0;
|
|
3221
|
+
}
|
|
3070
3222
|
return available;
|
|
3071
3223
|
} catch (error) {
|
|
3072
3224
|
this.health.available = false;
|
|
3073
3225
|
this.health.errorRate = 1;
|
|
3074
3226
|
this.health.lastCheck = Date.now();
|
|
3075
|
-
this.health.consecutiveFailures
|
|
3227
|
+
this.health.consecutiveFailures++;
|
|
3228
|
+
this.health.consecutiveSuccesses = 0;
|
|
3076
3229
|
return false;
|
|
3077
3230
|
}
|
|
3078
3231
|
}
|
|
@@ -3127,26 +3280,16 @@ ${fullPrompt}
|
|
|
3127
3280
|
* @returns true if this is a rate limit or quota error
|
|
3128
3281
|
*/
|
|
3129
3282
|
detectRateLimitError(error) {
|
|
3130
|
-
const
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
if (isLimited) {
|
|
3134
|
-
this.logger.debug("Rate limit error detected", {
|
|
3135
|
-
provider: this.config.name,
|
|
3136
|
-
message: error?.message,
|
|
3137
|
-
code: error?.code,
|
|
3138
|
-
status: error?.status || error?.statusCode
|
|
3139
|
-
});
|
|
3140
|
-
}
|
|
3141
|
-
return isLimited;
|
|
3142
|
-
} catch (detectionError) {
|
|
3143
|
-
this.logger.warn("Rate limit detection failed, using fallback", {
|
|
3283
|
+
const isLimited = isLimitError(error, this.config.name);
|
|
3284
|
+
if (isLimited) {
|
|
3285
|
+
this.logger.debug("Rate limit error detected", {
|
|
3144
3286
|
provider: this.config.name,
|
|
3145
|
-
|
|
3287
|
+
message: error?.message,
|
|
3288
|
+
code: error?.code,
|
|
3289
|
+
status: error?.status || error?.statusCode
|
|
3146
3290
|
});
|
|
3147
|
-
const message = (error?.message || "").toLowerCase();
|
|
3148
|
-
return message.includes("rate limit") || message.includes("quota") || message.includes("resource_exhausted") || message.includes("too many requests");
|
|
3149
3291
|
}
|
|
3292
|
+
return isLimited;
|
|
3150
3293
|
}
|
|
3151
3294
|
/**
|
|
3152
3295
|
* Escape shell command arguments to prevent injection
|
|
@@ -3239,8 +3382,20 @@ ${fullPrompt}
|
|
|
3239
3382
|
};
|
|
3240
3383
|
}
|
|
3241
3384
|
shouldRetry(error) {
|
|
3242
|
-
const
|
|
3243
|
-
|
|
3385
|
+
const providerName = this.config.name.toLowerCase();
|
|
3386
|
+
let retryableProvider = "base";
|
|
3387
|
+
if (providerName === "claude" || providerName === "claude-code") {
|
|
3388
|
+
retryableProvider = "claude";
|
|
3389
|
+
} else if (providerName === "gemini" || providerName === "gemini-cli") {
|
|
3390
|
+
retryableProvider = "gemini";
|
|
3391
|
+
} else if (providerName === "openai") {
|
|
3392
|
+
retryableProvider = "openai";
|
|
3393
|
+
} else if (providerName === "codex") {
|
|
3394
|
+
retryableProvider = "codex";
|
|
3395
|
+
} else if (providerName === "ax-cli" || providerName === "glm") {
|
|
3396
|
+
retryableProvider = "ax-cli";
|
|
3397
|
+
}
|
|
3398
|
+
return shouldRetryError(error, retryableProvider);
|
|
3244
3399
|
}
|
|
3245
3400
|
getRetryDelay(attempt) {
|
|
3246
3401
|
return Math.min(1e3 * Math.pow(2, attempt - 1), 3e4);
|
|
@@ -3264,7 +3419,9 @@ ${fullPrompt}
|
|
|
3264
3419
|
},
|
|
3265
3420
|
health: {
|
|
3266
3421
|
consecutiveFailures: this.health.consecutiveFailures,
|
|
3267
|
-
|
|
3422
|
+
// BUG FIX: Use actual tracked value instead of incorrect derivation
|
|
3423
|
+
// Previously: `consecutiveFailures === 0 ? 1 : 0` which was wrong
|
|
3424
|
+
consecutiveSuccesses: this.health.consecutiveSuccesses,
|
|
3268
3425
|
lastCheckTime: this.health.lastCheck,
|
|
3269
3426
|
lastCheckDuration: 0,
|
|
3270
3427
|
uptime: this.health.lastCheck > 0 ? Date.now() - this.health.lastCheck : 0
|
|
@@ -3964,7 +4121,20 @@ var init_sdk_adapter = __esm({
|
|
|
3964
4121
|
}
|
|
3965
4122
|
}
|
|
3966
4123
|
const tokenCount = result.usage ? (result.usage.input_tokens || 0) + (result.usage.output_tokens || 0) : void 0;
|
|
3967
|
-
if (!this.options.reuseThreads) {
|
|
4124
|
+
if (!this.options.reuseThreads && this.activeThread) {
|
|
4125
|
+
try {
|
|
4126
|
+
const threadWithDispose = this.activeThread;
|
|
4127
|
+
if (typeof threadWithDispose.dispose === "function") {
|
|
4128
|
+
const disposeResult = threadWithDispose.dispose();
|
|
4129
|
+
if (disposeResult instanceof Promise) {
|
|
4130
|
+
await disposeResult;
|
|
4131
|
+
}
|
|
4132
|
+
}
|
|
4133
|
+
} catch (disposeError) {
|
|
4134
|
+
logger.warn("Error disposing thread", {
|
|
4135
|
+
error: disposeError instanceof Error ? disposeError.message : String(disposeError)
|
|
4136
|
+
});
|
|
4137
|
+
}
|
|
3968
4138
|
this.activeThread = null;
|
|
3969
4139
|
}
|
|
3970
4140
|
return {
|
|
@@ -4143,12 +4313,38 @@ var init_hybrid_adapter = __esm({
|
|
|
4143
4313
|
}
|
|
4144
4314
|
this.activeMode = "cli";
|
|
4145
4315
|
}
|
|
4316
|
+
/**
|
|
4317
|
+
* Cleanup resources
|
|
4318
|
+
*
|
|
4319
|
+
* BUG FIX: Use Promise.allSettled to ensure cleanup continues even if one
|
|
4320
|
+
* adapter fails, and properly log any cleanup errors. Previously, a failure
|
|
4321
|
+
* in SDK destroy would prevent CLI cleanup from running.
|
|
4322
|
+
*/
|
|
4146
4323
|
async destroy() {
|
|
4147
|
-
|
|
4148
|
-
if (this.
|
|
4324
|
+
const cleanupPromises = [];
|
|
4325
|
+
if (this.sdkAdapter) {
|
|
4326
|
+
cleanupPromises.push(
|
|
4327
|
+
this.sdkAdapter.destroy().catch((err) => {
|
|
4328
|
+
logger.warn("Error destroying SDK adapter", {
|
|
4329
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4330
|
+
});
|
|
4331
|
+
})
|
|
4332
|
+
);
|
|
4333
|
+
}
|
|
4334
|
+
if (this.cliAdapter) {
|
|
4335
|
+
cleanupPromises.push(
|
|
4336
|
+
this.cliAdapter.cleanup().catch((err) => {
|
|
4337
|
+
logger.warn("Error cleaning up CLI adapter", {
|
|
4338
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4339
|
+
});
|
|
4340
|
+
})
|
|
4341
|
+
);
|
|
4342
|
+
}
|
|
4343
|
+
await Promise.allSettled(cleanupPromises);
|
|
4149
4344
|
this.sdkAdapter = null;
|
|
4150
4345
|
this.cliAdapter = null;
|
|
4151
4346
|
this.activeMode = null;
|
|
4347
|
+
logger.debug("HybridCodexAdapter destroyed");
|
|
4152
4348
|
}
|
|
4153
4349
|
};
|
|
4154
4350
|
}
|
|
@@ -4205,7 +4401,7 @@ var init_openai_provider = __esm({
|
|
|
4205
4401
|
error: error instanceof Error ? error.message : String(error),
|
|
4206
4402
|
mode: this.hybridAdapter?.getActiveMode() || "unknown"
|
|
4207
4403
|
});
|
|
4208
|
-
throw error;
|
|
4404
|
+
throw this.handleError(error);
|
|
4209
4405
|
}
|
|
4210
4406
|
}
|
|
4211
4407
|
initializeHybridAdapter() {
|
|
@@ -4398,17 +4594,25 @@ var init_command_builder = __esm({
|
|
|
4398
4594
|
/**
|
|
4399
4595
|
* Escape string for shell execution
|
|
4400
4596
|
*
|
|
4401
|
-
*
|
|
4402
|
-
*
|
|
4597
|
+
* Uses $'...' ANSI-C quoting for reliable cross-platform escaping.
|
|
4598
|
+
* This handles all special characters including newlines, tabs, and unicode.
|
|
4599
|
+
*
|
|
4600
|
+
* Escapes:
|
|
4601
|
+
* - Single quotes: ' → \'
|
|
4403
4602
|
* - Backslashes: \ → \\
|
|
4404
|
-
* -
|
|
4405
|
-
* -
|
|
4603
|
+
* - Newlines: \n → \n (literal escape sequence)
|
|
4604
|
+
* - Carriage returns: \r → \r
|
|
4605
|
+
* - Tabs: \t → \t
|
|
4606
|
+
*
|
|
4607
|
+
* BUG FIX (v11.3.3): Previous implementation using double quotes didn't handle
|
|
4608
|
+
* newlines properly, which could break shell commands or cause unexpected behavior.
|
|
4406
4609
|
*
|
|
4407
4610
|
* @param input - String to escape
|
|
4408
|
-
* @returns Shell-safe escaped string
|
|
4611
|
+
* @returns Shell-safe escaped string using ANSI-C quoting
|
|
4409
4612
|
*/
|
|
4410
4613
|
escape(input) {
|
|
4411
|
-
|
|
4614
|
+
const escaped = input.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
4615
|
+
return `$'${escaped}'`;
|
|
4412
4616
|
}
|
|
4413
4617
|
/**
|
|
4414
4618
|
* Build environment variables for CLI execution
|
|
@@ -4874,24 +5078,25 @@ var init_token_estimator = __esm({
|
|
|
4874
5078
|
};
|
|
4875
5079
|
}
|
|
4876
5080
|
});
|
|
4877
|
-
|
|
4878
|
-
// src/integrations/ax-cli-sdk/subagent-adapter.ts
|
|
4879
|
-
var TOKEN_SPLIT, SubagentAdapter;
|
|
5081
|
+
var TOKEN_SPLIT, DEFAULT_MAX_PARALLEL, DEFAULT_TIMEOUT_MS, DEFAULT_SUBAGENT_MAX_TOOL_ROUNDS, SubagentAdapter;
|
|
4880
5082
|
var init_subagent_adapter = __esm({
|
|
4881
5083
|
"src/integrations/ax-cli-sdk/subagent-adapter.ts"() {
|
|
4882
5084
|
init_esm_shims();
|
|
4883
5085
|
init_logger();
|
|
4884
5086
|
TOKEN_SPLIT = { PROMPT: 0.3, COMPLETION: 0.7 };
|
|
5087
|
+
DEFAULT_MAX_PARALLEL = 4;
|
|
5088
|
+
DEFAULT_TIMEOUT_MS = 3e5;
|
|
5089
|
+
DEFAULT_SUBAGENT_MAX_TOOL_ROUNDS = 100;
|
|
4885
5090
|
SubagentAdapter = class {
|
|
4886
5091
|
orchestrator = null;
|
|
4887
5092
|
subagents = /* @__PURE__ */ new Map();
|
|
5093
|
+
busySubagents = /* @__PURE__ */ new Set();
|
|
4888
5094
|
sdkAvailable = null;
|
|
4889
5095
|
options;
|
|
4890
5096
|
constructor(options = {}) {
|
|
4891
5097
|
this.options = {
|
|
4892
|
-
maxParallel: options.maxParallel ??
|
|
4893
|
-
timeout: options.timeout ??
|
|
4894
|
-
// 5 minutes
|
|
5098
|
+
maxParallel: options.maxParallel ?? DEFAULT_MAX_PARALLEL,
|
|
5099
|
+
timeout: options.timeout ?? DEFAULT_TIMEOUT_MS,
|
|
4895
5100
|
sharedContext: options.sharedContext ?? true,
|
|
4896
5101
|
continueOnError: options.continueOnError ?? true,
|
|
4897
5102
|
events: options.events,
|
|
@@ -4946,23 +5151,41 @@ var init_subagent_adapter = __esm({
|
|
|
4946
5151
|
taskCount: tasks.length,
|
|
4947
5152
|
maxParallel: this.options.maxParallel
|
|
4948
5153
|
});
|
|
4949
|
-
const
|
|
4950
|
-
const
|
|
5154
|
+
const tasksWithIndex = tasks.map((task, index) => ({ task, index }));
|
|
5155
|
+
const sortedTasks = [...tasksWithIndex].sort(
|
|
5156
|
+
(a, b) => (a.task.priority ?? 0) - (b.task.priority ?? 0)
|
|
5157
|
+
);
|
|
5158
|
+
const results = new Array(tasks.length);
|
|
4951
5159
|
const batchSize = this.options.maxParallel;
|
|
4952
5160
|
for (let i = 0; i < sortedTasks.length; i += batchSize) {
|
|
4953
5161
|
const batch = sortedTasks.slice(i, i + batchSize);
|
|
4954
|
-
const batchResults = await this.executeBatch(batch);
|
|
4955
|
-
|
|
5162
|
+
const batchResults = await this.executeBatch(batch.map((item) => item.task));
|
|
5163
|
+
batch.forEach((item, idx) => {
|
|
5164
|
+
results[item.index] = batchResults[idx];
|
|
5165
|
+
});
|
|
4956
5166
|
if (this.options.sharedContext && i + batchSize < sortedTasks.length) {
|
|
4957
5167
|
await this.propagateContext(batchResults);
|
|
4958
5168
|
}
|
|
4959
5169
|
}
|
|
5170
|
+
const finalResults = results.map((result, index) => {
|
|
5171
|
+
if (result) {
|
|
5172
|
+
return result;
|
|
5173
|
+
}
|
|
5174
|
+
return {
|
|
5175
|
+
task: tasks[index]?.task ?? "unknown",
|
|
5176
|
+
role: tasks[index]?.config.role ?? "custom",
|
|
5177
|
+
content: "",
|
|
5178
|
+
success: false,
|
|
5179
|
+
error: "Subagent result missing",
|
|
5180
|
+
latencyMs: 0
|
|
5181
|
+
};
|
|
5182
|
+
});
|
|
4960
5183
|
logger.info("Parallel execution complete", {
|
|
4961
5184
|
taskCount: tasks.length,
|
|
4962
|
-
successCount:
|
|
5185
|
+
successCount: finalResults.filter((r) => r.success).length,
|
|
4963
5186
|
totalLatencyMs: Date.now() - startTime
|
|
4964
5187
|
});
|
|
4965
|
-
return
|
|
5188
|
+
return finalResults;
|
|
4966
5189
|
}
|
|
4967
5190
|
/**
|
|
4968
5191
|
* Execute tasks sequentially with context propagation
|
|
@@ -4980,18 +5203,27 @@ var init_subagent_adapter = __esm({
|
|
|
4980
5203
|
const results = [];
|
|
4981
5204
|
let accumulatedContext = "";
|
|
4982
5205
|
for (const task of tasks) {
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
5206
|
+
let combinedContext;
|
|
5207
|
+
if (task.context && accumulatedContext) {
|
|
5208
|
+
combinedContext = `${task.context}
|
|
4986
5209
|
|
|
4987
5210
|
Previous results:
|
|
4988
|
-
${accumulatedContext}
|
|
5211
|
+
${accumulatedContext}`;
|
|
5212
|
+
} else if (task.context) {
|
|
5213
|
+
combinedContext = task.context;
|
|
5214
|
+
} else if (accumulatedContext) {
|
|
5215
|
+
combinedContext = accumulatedContext;
|
|
5216
|
+
}
|
|
5217
|
+
const taskWithContext = {
|
|
5218
|
+
...task,
|
|
5219
|
+
context: combinedContext
|
|
4989
5220
|
};
|
|
4990
5221
|
const result = await this.executeTask(taskWithContext);
|
|
4991
5222
|
results.push(result);
|
|
4992
5223
|
if (result.success && this.options.sharedContext) {
|
|
5224
|
+
const truncated = result.content.length > 500;
|
|
4993
5225
|
accumulatedContext += `
|
|
4994
|
-
[${result.role}]: ${result.content.substring(0, 500)}...
|
|
5226
|
+
[${result.role}]: ${result.content.substring(0, 500)}${truncated ? "..." : ""}
|
|
4995
5227
|
`;
|
|
4996
5228
|
}
|
|
4997
5229
|
if (!result.success && !this.options.continueOnError) {
|
|
@@ -5017,6 +5249,7 @@ ${accumulatedContext}` : accumulatedContext || void 0
|
|
|
5017
5249
|
const startTime = Date.now();
|
|
5018
5250
|
const subagentKey = this.getSubagentKey(task.config);
|
|
5019
5251
|
const taskId = `${subagentKey}:${Date.now()}`;
|
|
5252
|
+
let release = null;
|
|
5020
5253
|
this.emitEvent("task_start", task.task);
|
|
5021
5254
|
this.emitEvent("state_change", {
|
|
5022
5255
|
previousState: "idle",
|
|
@@ -5029,7 +5262,9 @@ ${accumulatedContext}` : accumulatedContext || void 0
|
|
|
5029
5262
|
message: `Starting ${task.config.role} task`
|
|
5030
5263
|
});
|
|
5031
5264
|
try {
|
|
5032
|
-
const
|
|
5265
|
+
const acquired = await this.acquireSubagent(task.config);
|
|
5266
|
+
const subagent = acquired.subagent;
|
|
5267
|
+
release = acquired.release;
|
|
5033
5268
|
const prompt = task.context ? `Context:
|
|
5034
5269
|
${task.context}
|
|
5035
5270
|
|
|
@@ -5120,6 +5355,10 @@ ${task.task}` : task.task;
|
|
|
5120
5355
|
error: errorMessage,
|
|
5121
5356
|
latencyMs
|
|
5122
5357
|
};
|
|
5358
|
+
} finally {
|
|
5359
|
+
if (release) {
|
|
5360
|
+
release();
|
|
5361
|
+
}
|
|
5123
5362
|
}
|
|
5124
5363
|
}
|
|
5125
5364
|
/**
|
|
@@ -5142,7 +5381,11 @@ ${task.task}` : task.task;
|
|
|
5142
5381
|
* Propagate context from completed tasks
|
|
5143
5382
|
*/
|
|
5144
5383
|
async propagateContext(results) {
|
|
5145
|
-
const contextSummary = results.filter((r) => r.success).map((r) =>
|
|
5384
|
+
const contextSummary = results.filter((r) => r.success).map((r) => {
|
|
5385
|
+
const truncated = r.content.length > 200;
|
|
5386
|
+
const summary = r.content.substring(0, 200);
|
|
5387
|
+
return `[${r.role}]: ${summary}${truncated ? "..." : ""}`;
|
|
5388
|
+
}).join("\n");
|
|
5146
5389
|
if (contextSummary && this.orchestrator?.setSharedContext) {
|
|
5147
5390
|
try {
|
|
5148
5391
|
await this.orchestrator.setSharedContext(contextSummary);
|
|
@@ -5168,7 +5411,7 @@ ${task.task}` : task.task;
|
|
|
5168
5411
|
const { createSubagent } = await import('@defai.digital/ax-cli/sdk');
|
|
5169
5412
|
const subagent = createSubagent(config.role, {
|
|
5170
5413
|
systemPrompt: config.instructions,
|
|
5171
|
-
maxToolRounds: config.maxToolRounds ??
|
|
5414
|
+
maxToolRounds: config.maxToolRounds ?? DEFAULT_SUBAGENT_MAX_TOOL_ROUNDS
|
|
5172
5415
|
});
|
|
5173
5416
|
this.subagents.set(key, subagent);
|
|
5174
5417
|
logger.debug("Created new subagent", {
|
|
@@ -5185,11 +5428,70 @@ ${task.task}` : task.task;
|
|
|
5185
5428
|
throw error;
|
|
5186
5429
|
}
|
|
5187
5430
|
}
|
|
5431
|
+
/**
|
|
5432
|
+
* Create a temporary subagent instance. Used when the cached subagent is busy.
|
|
5433
|
+
*/
|
|
5434
|
+
async createEphemeralSubagent(config) {
|
|
5435
|
+
const { createSubagent } = await import('@defai.digital/ax-cli/sdk');
|
|
5436
|
+
return createSubagent(config.role, {
|
|
5437
|
+
systemPrompt: config.instructions,
|
|
5438
|
+
maxToolRounds: config.maxToolRounds ?? DEFAULT_SUBAGENT_MAX_TOOL_ROUNDS
|
|
5439
|
+
});
|
|
5440
|
+
}
|
|
5441
|
+
/**
|
|
5442
|
+
* Acquire a subagent instance for a task. If a cached instance is already
|
|
5443
|
+
* running another task, create a short-lived one to avoid interleaved
|
|
5444
|
+
* streams from concurrent executions.
|
|
5445
|
+
*/
|
|
5446
|
+
async acquireSubagent(config) {
|
|
5447
|
+
const key = this.getSubagentKey(config);
|
|
5448
|
+
if (!this.busySubagents.has(key)) {
|
|
5449
|
+
this.busySubagents.add(key);
|
|
5450
|
+
let subagent;
|
|
5451
|
+
try {
|
|
5452
|
+
subagent = await this.getOrCreateSubagent(config);
|
|
5453
|
+
} catch (error) {
|
|
5454
|
+
this.busySubagents.delete(key);
|
|
5455
|
+
throw error;
|
|
5456
|
+
}
|
|
5457
|
+
return {
|
|
5458
|
+
subagent,
|
|
5459
|
+
release: () => this.releaseSubagent(key)
|
|
5460
|
+
};
|
|
5461
|
+
}
|
|
5462
|
+
logger.debug("Cached subagent busy, creating ephemeral instance", { key });
|
|
5463
|
+
const ephemeral = await this.createEphemeralSubagent(config);
|
|
5464
|
+
return {
|
|
5465
|
+
subagent: ephemeral,
|
|
5466
|
+
release: () => {
|
|
5467
|
+
try {
|
|
5468
|
+
if (ephemeral.dispose) {
|
|
5469
|
+
const result = ephemeral.dispose();
|
|
5470
|
+
if (result instanceof Promise) {
|
|
5471
|
+
result.catch((err) => logger.warn("Error disposing ephemeral subagent", { error: err instanceof Error ? err.message : String(err) }));
|
|
5472
|
+
}
|
|
5473
|
+
}
|
|
5474
|
+
} catch (error) {
|
|
5475
|
+
logger.warn("Error disposing ephemeral subagent", {
|
|
5476
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5477
|
+
});
|
|
5478
|
+
}
|
|
5479
|
+
}
|
|
5480
|
+
};
|
|
5481
|
+
}
|
|
5482
|
+
/**
|
|
5483
|
+
* Release a cached subagent so it can be reused.
|
|
5484
|
+
*/
|
|
5485
|
+
releaseSubagent(key) {
|
|
5486
|
+
this.busySubagents.delete(key);
|
|
5487
|
+
}
|
|
5188
5488
|
/**
|
|
5189
5489
|
* Generate unique key for subagent config
|
|
5190
5490
|
*/
|
|
5191
5491
|
getSubagentKey(config) {
|
|
5192
|
-
|
|
5492
|
+
const instructionsHash = config.instructions ? createHash("sha256").update(config.instructions).digest("hex").slice(0, 12) : "noinstr";
|
|
5493
|
+
const maxToolRounds = config.maxToolRounds ?? DEFAULT_SUBAGENT_MAX_TOOL_ROUNDS;
|
|
5494
|
+
return `${config.role}:${config.specialization || "default"}:${maxToolRounds}:${instructionsHash}`;
|
|
5193
5495
|
}
|
|
5194
5496
|
/**
|
|
5195
5497
|
* Ensure orchestrator is initialized
|
|
@@ -5245,8 +5547,11 @@ ${task.task}` : task.task;
|
|
|
5245
5547
|
}
|
|
5246
5548
|
/**
|
|
5247
5549
|
* Cleanup all subagents
|
|
5550
|
+
* BUG FIX: Continue cleanup even if individual subagent disposal fails,
|
|
5551
|
+
* and report all errors at the end to ensure no resources are leaked.
|
|
5248
5552
|
*/
|
|
5249
5553
|
async destroy() {
|
|
5554
|
+
const errors = [];
|
|
5250
5555
|
for (const [key, subagent] of this.subagents) {
|
|
5251
5556
|
try {
|
|
5252
5557
|
if (subagent.dispose) {
|
|
@@ -5257,24 +5562,54 @@ ${task.task}` : task.task;
|
|
|
5257
5562
|
}
|
|
5258
5563
|
logger.debug("Subagent disposed", { key });
|
|
5259
5564
|
} catch (error) {
|
|
5565
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
5260
5566
|
logger.warn("Error disposing subagent", {
|
|
5261
5567
|
key,
|
|
5262
|
-
error:
|
|
5568
|
+
error: errorMsg
|
|
5263
5569
|
});
|
|
5570
|
+
errors.push({ key, error: errorMsg });
|
|
5264
5571
|
}
|
|
5265
5572
|
}
|
|
5573
|
+
const orchestrator = this.orchestrator;
|
|
5266
5574
|
this.subagents.clear();
|
|
5575
|
+
this.busySubagents.clear();
|
|
5576
|
+
if (orchestrator && typeof orchestrator.dispose === "function") {
|
|
5577
|
+
try {
|
|
5578
|
+
await orchestrator.dispose();
|
|
5579
|
+
} catch (error) {
|
|
5580
|
+
logger.warn("Error disposing subagent orchestrator", {
|
|
5581
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5582
|
+
});
|
|
5583
|
+
}
|
|
5584
|
+
} else if (orchestrator && typeof orchestrator.shutdown === "function") {
|
|
5585
|
+
try {
|
|
5586
|
+
await orchestrator.shutdown();
|
|
5587
|
+
} catch (error) {
|
|
5588
|
+
logger.warn("Error shutting down subagent orchestrator", {
|
|
5589
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5590
|
+
});
|
|
5591
|
+
}
|
|
5592
|
+
}
|
|
5267
5593
|
this.orchestrator = null;
|
|
5594
|
+
if (errors.length > 0) {
|
|
5595
|
+
logger.error("SubagentAdapter cleanup had errors", {
|
|
5596
|
+
errorCount: errors.length,
|
|
5597
|
+
errors
|
|
5598
|
+
});
|
|
5599
|
+
}
|
|
5268
5600
|
logger.debug("SubagentAdapter destroyed");
|
|
5269
5601
|
}
|
|
5270
5602
|
};
|
|
5271
5603
|
}
|
|
5272
5604
|
});
|
|
5273
|
-
var CheckpointAdapter;
|
|
5605
|
+
var DEFAULT_CHECKPOINT_DIR, DEFAULT_MAX_CHECKPOINTS, FLUSH_TIMEOUT_MS, CheckpointAdapter;
|
|
5274
5606
|
var init_checkpoint_adapter = __esm({
|
|
5275
5607
|
"src/integrations/ax-cli-sdk/checkpoint-adapter.ts"() {
|
|
5276
5608
|
init_esm_shims();
|
|
5277
5609
|
init_logger();
|
|
5610
|
+
DEFAULT_CHECKPOINT_DIR = ".automatosx/checkpoints";
|
|
5611
|
+
DEFAULT_MAX_CHECKPOINTS = 10;
|
|
5612
|
+
FLUSH_TIMEOUT_MS = 5e3;
|
|
5278
5613
|
CheckpointAdapter = class {
|
|
5279
5614
|
checkpointDir;
|
|
5280
5615
|
sdkCheckpointManager = null;
|
|
@@ -5282,11 +5617,12 @@ var init_checkpoint_adapter = __esm({
|
|
|
5282
5617
|
options;
|
|
5283
5618
|
autoSaveTimer = null;
|
|
5284
5619
|
pendingCheckpoint = null;
|
|
5620
|
+
fsLock = Promise.resolve();
|
|
5285
5621
|
constructor(options = {}) {
|
|
5286
5622
|
this.options = {
|
|
5287
|
-
checkpointDir: options.checkpointDir ??
|
|
5623
|
+
checkpointDir: options.checkpointDir ?? DEFAULT_CHECKPOINT_DIR,
|
|
5288
5624
|
autoSaveInterval: options.autoSaveInterval ?? 0,
|
|
5289
|
-
maxCheckpoints: options.maxCheckpoints ??
|
|
5625
|
+
maxCheckpoints: options.maxCheckpoints ?? DEFAULT_MAX_CHECKPOINTS,
|
|
5290
5626
|
compress: options.compress ?? false
|
|
5291
5627
|
};
|
|
5292
5628
|
this.checkpointDir = this.options.checkpointDir;
|
|
@@ -5328,8 +5664,10 @@ var init_checkpoint_adapter = __esm({
|
|
|
5328
5664
|
});
|
|
5329
5665
|
}
|
|
5330
5666
|
}
|
|
5331
|
-
await this.
|
|
5332
|
-
|
|
5667
|
+
await this.withFsLock(async () => {
|
|
5668
|
+
await this.saveToFileSystem(checkpoint);
|
|
5669
|
+
await this.cleanupOldCheckpoints(workflowId);
|
|
5670
|
+
});
|
|
5333
5671
|
logger.info("Checkpoint saved", {
|
|
5334
5672
|
workflowId,
|
|
5335
5673
|
phase: checkpoint.phase,
|
|
@@ -5387,23 +5725,32 @@ var init_checkpoint_adapter = __esm({
|
|
|
5387
5725
|
});
|
|
5388
5726
|
}
|
|
5389
5727
|
}
|
|
5390
|
-
await this.deleteFromFileSystem(workflowId);
|
|
5728
|
+
await this.withFsLock(() => this.deleteFromFileSystem(workflowId));
|
|
5391
5729
|
logger.info("Checkpoints deleted", { workflowId });
|
|
5392
5730
|
}
|
|
5393
5731
|
/**
|
|
5394
5732
|
* List all checkpoints for a workflow
|
|
5733
|
+
*
|
|
5734
|
+
* BUG FIX (v11.3.3): The previous pattern `startsWith(workflowId-)` would match
|
|
5735
|
+
* similar workflow IDs. For example, "auth" would match "auth-v2-1.json".
|
|
5736
|
+
* Now we verify that the suffix after the workflowId is a numeric phase.
|
|
5395
5737
|
*/
|
|
5396
5738
|
async list(workflowId) {
|
|
5397
5739
|
try {
|
|
5398
|
-
await
|
|
5399
|
-
const files = await
|
|
5400
|
-
const
|
|
5401
|
-
|
|
5402
|
-
|
|
5740
|
+
await fs5.mkdir(this.checkpointDir, { recursive: true });
|
|
5741
|
+
const files = await fs5.readdir(this.checkpointDir);
|
|
5742
|
+
const prefix = `${workflowId}-`;
|
|
5743
|
+
const matchingFiles = files.filter((file) => {
|
|
5744
|
+
if (!file.startsWith(prefix) || !file.endsWith(".json")) {
|
|
5745
|
+
return false;
|
|
5746
|
+
}
|
|
5747
|
+
const middle = file.slice(prefix.length, -5);
|
|
5748
|
+
return /^\d+$/.test(middle);
|
|
5749
|
+
});
|
|
5403
5750
|
const results = await Promise.all(
|
|
5404
5751
|
matchingFiles.map(async (file) => {
|
|
5405
5752
|
try {
|
|
5406
|
-
const content = await
|
|
5753
|
+
const content = await fs5.readFile(
|
|
5407
5754
|
path4.join(this.checkpointDir, file),
|
|
5408
5755
|
"utf-8"
|
|
5409
5756
|
);
|
|
@@ -5433,6 +5780,13 @@ var init_checkpoint_adapter = __esm({
|
|
|
5433
5780
|
async savePhase(workflowId, phaseId, result, workflow) {
|
|
5434
5781
|
let checkpoint = await this.load(workflowId);
|
|
5435
5782
|
const phaseIndex = workflow.phases.findIndex((p) => p.id === phaseId);
|
|
5783
|
+
if (phaseIndex === -1) {
|
|
5784
|
+
logger.warn("Phase not found in workflow", {
|
|
5785
|
+
workflowId,
|
|
5786
|
+
phaseId,
|
|
5787
|
+
availablePhases: workflow.phases.map((p) => p.id)
|
|
5788
|
+
});
|
|
5789
|
+
}
|
|
5436
5790
|
if (!checkpoint) {
|
|
5437
5791
|
checkpoint = {
|
|
5438
5792
|
id: `${workflowId}-${Date.now()}`,
|
|
@@ -5449,9 +5803,12 @@ var init_checkpoint_adapter = __esm({
|
|
|
5449
5803
|
}
|
|
5450
5804
|
if (result.success) {
|
|
5451
5805
|
checkpoint.completedTasks.push(phaseId);
|
|
5452
|
-
|
|
5806
|
+
if (phaseIndex >= 0) {
|
|
5807
|
+
checkpoint.phase = phaseIndex + 1;
|
|
5808
|
+
}
|
|
5809
|
+
const phaseLabel = phaseIndex >= 0 ? `Phase ${phaseIndex + 1}` : "Unknown Phase";
|
|
5453
5810
|
checkpoint.context += `
|
|
5454
|
-
[
|
|
5811
|
+
[${phaseLabel}: ${phaseId}]
|
|
5455
5812
|
${result.content.substring(0, 1e3)}
|
|
5456
5813
|
`;
|
|
5457
5814
|
if (result.tokensUsed && checkpoint.tokensUsed) {
|
|
@@ -5498,11 +5855,11 @@ ${result.content.substring(0, 1e3)}
|
|
|
5498
5855
|
await this.sdkCheckpointManager.delete(workflowId);
|
|
5499
5856
|
}
|
|
5500
5857
|
async saveToFileSystem(checkpoint) {
|
|
5501
|
-
await
|
|
5858
|
+
await fs5.mkdir(this.checkpointDir, { recursive: true });
|
|
5502
5859
|
const filename = `${checkpoint.workflowId}-${checkpoint.phase}.json`;
|
|
5503
5860
|
const filepath = path4.join(this.checkpointDir, filename);
|
|
5504
5861
|
const content = JSON.stringify(checkpoint, null, 2);
|
|
5505
|
-
await
|
|
5862
|
+
await this.atomicWrite(filepath, content);
|
|
5506
5863
|
}
|
|
5507
5864
|
async loadFromFileSystem(workflowId) {
|
|
5508
5865
|
try {
|
|
@@ -5512,12 +5869,21 @@ ${result.content.substring(0, 1e3)}
|
|
|
5512
5869
|
return null;
|
|
5513
5870
|
}
|
|
5514
5871
|
}
|
|
5872
|
+
/**
|
|
5873
|
+
* BUG FIX (v11.3.3): Use same precise matching as list() to avoid
|
|
5874
|
+
* deleting checkpoints for workflows with similar IDs.
|
|
5875
|
+
*/
|
|
5515
5876
|
async deleteFromFileSystem(workflowId) {
|
|
5516
5877
|
try {
|
|
5517
|
-
const files = await
|
|
5878
|
+
const files = await fs5.readdir(this.checkpointDir);
|
|
5879
|
+
const prefix = `${workflowId}-`;
|
|
5518
5880
|
for (const file of files) {
|
|
5519
|
-
if (file.startsWith(
|
|
5520
|
-
|
|
5881
|
+
if (!file.startsWith(prefix) || !file.endsWith(".json")) {
|
|
5882
|
+
continue;
|
|
5883
|
+
}
|
|
5884
|
+
const middle = file.slice(prefix.length, -5);
|
|
5885
|
+
if (/^\d+$/.test(middle)) {
|
|
5886
|
+
await fs5.unlink(path4.join(this.checkpointDir, file));
|
|
5521
5887
|
}
|
|
5522
5888
|
}
|
|
5523
5889
|
} catch (error) {
|
|
@@ -5535,7 +5901,7 @@ ${result.content.substring(0, 1e3)}
|
|
|
5535
5901
|
for (const checkpoint of toDelete) {
|
|
5536
5902
|
try {
|
|
5537
5903
|
const filename = `${checkpoint.workflowId}-${checkpoint.phase}.json`;
|
|
5538
|
-
await
|
|
5904
|
+
await fs5.unlink(path4.join(this.checkpointDir, filename));
|
|
5539
5905
|
} catch {
|
|
5540
5906
|
}
|
|
5541
5907
|
}
|
|
@@ -5559,8 +5925,11 @@ ${result.content.substring(0, 1e3)}
|
|
|
5559
5925
|
}
|
|
5560
5926
|
}
|
|
5561
5927
|
setupAutoSave() {
|
|
5928
|
+
let autoSaveRunning = false;
|
|
5562
5929
|
this.autoSaveTimer = setInterval(async () => {
|
|
5930
|
+
if (autoSaveRunning) return;
|
|
5563
5931
|
if (this.pendingCheckpoint) {
|
|
5932
|
+
autoSaveRunning = true;
|
|
5564
5933
|
try {
|
|
5565
5934
|
await this.save(
|
|
5566
5935
|
this.pendingCheckpoint.workflowId,
|
|
@@ -5571,6 +5940,8 @@ ${result.content.substring(0, 1e3)}
|
|
|
5571
5940
|
logger.warn("Auto-save failed", {
|
|
5572
5941
|
error: error instanceof Error ? error.message : String(error)
|
|
5573
5942
|
});
|
|
5943
|
+
} finally {
|
|
5944
|
+
autoSaveRunning = false;
|
|
5574
5945
|
}
|
|
5575
5946
|
}
|
|
5576
5947
|
}, this.options.autoSaveInterval);
|
|
@@ -5584,6 +5955,7 @@ ${result.content.substring(0, 1e3)}
|
|
|
5584
5955
|
/**
|
|
5585
5956
|
* Cleanup resources
|
|
5586
5957
|
* BUG FIX: Flush pending checkpoint before destroying to prevent data loss
|
|
5958
|
+
* BUG FIX: Add timeout to prevent hanging during cleanup
|
|
5587
5959
|
*/
|
|
5588
5960
|
async destroy() {
|
|
5589
5961
|
if (this.autoSaveTimer) {
|
|
@@ -5592,10 +5964,15 @@ ${result.content.substring(0, 1e3)}
|
|
|
5592
5964
|
}
|
|
5593
5965
|
if (this.pendingCheckpoint) {
|
|
5594
5966
|
try {
|
|
5595
|
-
await
|
|
5596
|
-
this.
|
|
5597
|
-
|
|
5598
|
-
|
|
5967
|
+
await Promise.race([
|
|
5968
|
+
this.save(
|
|
5969
|
+
this.pendingCheckpoint.workflowId,
|
|
5970
|
+
this.pendingCheckpoint
|
|
5971
|
+
),
|
|
5972
|
+
new Promise(
|
|
5973
|
+
(_, reject) => setTimeout(() => reject(new Error("Checkpoint flush timed out")), FLUSH_TIMEOUT_MS)
|
|
5974
|
+
)
|
|
5975
|
+
]);
|
|
5599
5976
|
logger.debug("Pending checkpoint flushed on destroy", {
|
|
5600
5977
|
workflowId: this.pendingCheckpoint.workflowId
|
|
5601
5978
|
});
|
|
@@ -5609,12 +5986,44 @@ ${result.content.substring(0, 1e3)}
|
|
|
5609
5986
|
this.sdkCheckpointManager = null;
|
|
5610
5987
|
logger.debug("CheckpointAdapter destroyed");
|
|
5611
5988
|
}
|
|
5989
|
+
/**
|
|
5990
|
+
* Serialize filesystem operations to avoid race conditions between auto-save and manual saves.
|
|
5991
|
+
*/
|
|
5992
|
+
async withFsLock(fn) {
|
|
5993
|
+
const resultPromise = this.fsLock.then(fn, fn);
|
|
5994
|
+
this.fsLock = resultPromise.then(
|
|
5995
|
+
() => Promise.resolve(),
|
|
5996
|
+
() => Promise.resolve()
|
|
5997
|
+
);
|
|
5998
|
+
return resultPromise;
|
|
5999
|
+
}
|
|
6000
|
+
/**
|
|
6001
|
+
* Write checkpoint atomically to avoid partial files being read by list/load.
|
|
6002
|
+
* BUG FIX: Clean up temp file on rename failure to prevent resource leak.
|
|
6003
|
+
*/
|
|
6004
|
+
async atomicWrite(filepath, content) {
|
|
6005
|
+
const tempFile = `${filepath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
6006
|
+
await fs5.writeFile(tempFile, content, "utf-8");
|
|
6007
|
+
try {
|
|
6008
|
+
await fs5.rename(tempFile, filepath);
|
|
6009
|
+
} catch (renameError) {
|
|
6010
|
+
try {
|
|
6011
|
+
await fs5.unlink(tempFile);
|
|
6012
|
+
} catch {
|
|
6013
|
+
}
|
|
6014
|
+
throw renameError;
|
|
6015
|
+
}
|
|
6016
|
+
}
|
|
5612
6017
|
};
|
|
5613
6018
|
}
|
|
5614
6019
|
});
|
|
5615
6020
|
function getInstructionsBridge(options) {
|
|
5616
6021
|
if (!defaultBridge) {
|
|
5617
6022
|
defaultBridge = new InstructionsBridge(options);
|
|
6023
|
+
} else if (options) {
|
|
6024
|
+
logger.warn("InstructionsBridge already initialized, ignoring new options", {
|
|
6025
|
+
hint: "Use resetInstructionsBridge() to recreate with new options"
|
|
6026
|
+
});
|
|
5618
6027
|
}
|
|
5619
6028
|
return defaultBridge;
|
|
5620
6029
|
}
|
|
@@ -5646,8 +6055,9 @@ var init_instructions_bridge = __esm({
|
|
|
5646
6055
|
* @returns Combined instructions from all sources
|
|
5647
6056
|
*/
|
|
5648
6057
|
async getInstructions(agentName, additionalContext) {
|
|
6058
|
+
const cacheSignature = await this.buildCacheSignature(agentName);
|
|
5649
6059
|
const cached = this.cache.get(agentName);
|
|
5650
|
-
if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
|
|
6060
|
+
if (cached && cached.signature === cacheSignature && Date.now() - cached.timestamp < this.CACHE_TTL) {
|
|
5651
6061
|
logger.debug("Using cached instructions", { agentName });
|
|
5652
6062
|
if (additionalContext) {
|
|
5653
6063
|
return {
|
|
@@ -5685,7 +6095,11 @@ var init_instructions_bridge = __esm({
|
|
|
5685
6095
|
projectMemory: !!projectContext
|
|
5686
6096
|
}
|
|
5687
6097
|
};
|
|
5688
|
-
this.cache.set(agentName, {
|
|
6098
|
+
this.cache.set(agentName, {
|
|
6099
|
+
instructions: combined,
|
|
6100
|
+
timestamp: Date.now(),
|
|
6101
|
+
signature: cacheSignature
|
|
6102
|
+
});
|
|
5689
6103
|
logger.info("Instructions combined", {
|
|
5690
6104
|
agentName,
|
|
5691
6105
|
sources: combined.sources,
|
|
@@ -5710,13 +6124,13 @@ var init_instructions_bridge = __esm({
|
|
|
5710
6124
|
if (additionalContext) {
|
|
5711
6125
|
parts.push("## Additional Context\n\n" + additionalContext);
|
|
5712
6126
|
}
|
|
5713
|
-
|
|
6127
|
+
const combined = parts.join("\n\n---\n\n");
|
|
5714
6128
|
if (combined.length > this.options.maxContextLength) {
|
|
5715
|
-
combined = combined.substring(0, this.options.maxContextLength - 100) + "\n\n[Context truncated due to length limit]";
|
|
5716
6129
|
logger.warn("Instructions truncated", {
|
|
5717
|
-
originalLength:
|
|
6130
|
+
originalLength: combined.length,
|
|
5718
6131
|
truncatedTo: this.options.maxContextLength
|
|
5719
6132
|
});
|
|
6133
|
+
return combined.substring(0, this.options.maxContextLength - 100) + "\n\n[Context truncated due to length limit]";
|
|
5720
6134
|
}
|
|
5721
6135
|
return combined;
|
|
5722
6136
|
}
|
|
@@ -5726,7 +6140,7 @@ var init_instructions_bridge = __esm({
|
|
|
5726
6140
|
async loadAgentProfile(agentName) {
|
|
5727
6141
|
const profilePath = path4.join(this.options.agentsDir, `${agentName}.ax.yaml`);
|
|
5728
6142
|
try {
|
|
5729
|
-
const content = await
|
|
6143
|
+
const content = await fs5.readFile(profilePath, "utf-8");
|
|
5730
6144
|
const profile = this.parseYamlProfile(content);
|
|
5731
6145
|
logger.debug("Agent profile loaded", { agentName, profilePath });
|
|
5732
6146
|
return profile;
|
|
@@ -5761,7 +6175,7 @@ var init_instructions_bridge = __esm({
|
|
|
5761
6175
|
}
|
|
5762
6176
|
}
|
|
5763
6177
|
try {
|
|
5764
|
-
const content = await
|
|
6178
|
+
const content = await fs5.readFile(this.options.axCliCustomPath, "utf-8");
|
|
5765
6179
|
logger.debug("Custom instructions loaded from file", {
|
|
5766
6180
|
path: this.options.axCliCustomPath
|
|
5767
6181
|
});
|
|
@@ -5805,6 +6219,11 @@ var init_instructions_bridge = __esm({
|
|
|
5805
6219
|
}
|
|
5806
6220
|
/**
|
|
5807
6221
|
* Parse YAML profile content (simple parser)
|
|
6222
|
+
*
|
|
6223
|
+
* BUG FIX (v11.3.3):
|
|
6224
|
+
* - Support keys with hyphens/underscores (e.g., display_name, my-key)
|
|
6225
|
+
* - Strip quotes from string values
|
|
6226
|
+
* - Handle empty values correctly
|
|
5808
6227
|
*/
|
|
5809
6228
|
parseYamlProfile(content) {
|
|
5810
6229
|
const lines = content.split("\n");
|
|
@@ -5813,27 +6232,33 @@ var init_instructions_bridge = __esm({
|
|
|
5813
6232
|
let instructionsBuffer = [];
|
|
5814
6233
|
let inInstructions = false;
|
|
5815
6234
|
for (const line of lines) {
|
|
5816
|
-
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
6235
|
+
const match = line.match(/^([\w-]+):\s*(.*)$/);
|
|
5817
6236
|
if (match) {
|
|
5818
|
-
const [,
|
|
5819
|
-
|
|
6237
|
+
const [, rawKey, rawValue] = match;
|
|
6238
|
+
const key = rawKey ?? "";
|
|
6239
|
+
let value = rawValue ?? "";
|
|
6240
|
+
if (value && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
|
|
6241
|
+
value = value.slice(1, -1);
|
|
6242
|
+
}
|
|
6243
|
+
const normalizedKey = key.replace(/-/g, "_");
|
|
6244
|
+
if (inInstructions && normalizedKey !== "instructions") {
|
|
5820
6245
|
profile.instructions = instructionsBuffer.join("\n").trim();
|
|
5821
6246
|
instructionsBuffer = [];
|
|
5822
6247
|
inInstructions = false;
|
|
5823
6248
|
}
|
|
5824
|
-
if (
|
|
6249
|
+
if (normalizedKey === "instructions") {
|
|
5825
6250
|
inInstructions = true;
|
|
5826
|
-
currentKey =
|
|
6251
|
+
currentKey = normalizedKey;
|
|
5827
6252
|
if (value && value.startsWith("|")) ; else if (value) {
|
|
5828
6253
|
profile.instructions = value;
|
|
5829
6254
|
inInstructions = false;
|
|
5830
6255
|
}
|
|
5831
|
-
} else if (
|
|
6256
|
+
} else if (normalizedKey === "expertise") {
|
|
5832
6257
|
profile.expertise = [];
|
|
5833
|
-
currentKey =
|
|
5834
|
-
} else if (
|
|
5835
|
-
profile[
|
|
5836
|
-
currentKey =
|
|
6258
|
+
currentKey = normalizedKey;
|
|
6259
|
+
} else if (normalizedKey && value !== void 0) {
|
|
6260
|
+
profile[normalizedKey] = value;
|
|
6261
|
+
currentKey = normalizedKey;
|
|
5837
6262
|
}
|
|
5838
6263
|
} else if (inInstructions) {
|
|
5839
6264
|
instructionsBuffer.push(line.replace(/^ /, ""));
|
|
@@ -5880,8 +6305,8 @@ ${profile.expertise.map((e) => `- ${e}`).join("\n")}
|
|
|
5880
6305
|
${profile.instructions}
|
|
5881
6306
|
`;
|
|
5882
6307
|
try {
|
|
5883
|
-
await
|
|
5884
|
-
await
|
|
6308
|
+
await fs5.mkdir(path4.dirname(this.options.axCliCustomPath), { recursive: true });
|
|
6309
|
+
await fs5.writeFile(this.options.axCliCustomPath, customContent, "utf-8");
|
|
5885
6310
|
logger.info("Agent synced to ax-cli custom instructions", {
|
|
5886
6311
|
agentName,
|
|
5887
6312
|
path: this.options.axCliCustomPath
|
|
@@ -5923,6 +6348,26 @@ ${profile.instructions}
|
|
|
5923
6348
|
this.cache.delete(agentName);
|
|
5924
6349
|
logger.debug("Agent cache cleared", { agentName });
|
|
5925
6350
|
}
|
|
6351
|
+
/**
|
|
6352
|
+
* Build a cache signature from underlying file mtimes to invalidate cache when
|
|
6353
|
+
* agent profiles or custom instructions change on disk.
|
|
6354
|
+
*/
|
|
6355
|
+
async buildCacheSignature(agentName) {
|
|
6356
|
+
const profilePath = path4.join(this.options.agentsDir, `${agentName}.ax.yaml`);
|
|
6357
|
+
const [profileSig, customSig] = await Promise.all([
|
|
6358
|
+
this.getFileSignature(profilePath),
|
|
6359
|
+
this.getFileSignature(this.options.axCliCustomPath)
|
|
6360
|
+
]);
|
|
6361
|
+
return [profileSig, customSig, this.options.includeProjectMemory ? "mem" : "nomem"].join("|");
|
|
6362
|
+
}
|
|
6363
|
+
async getFileSignature(filePath) {
|
|
6364
|
+
try {
|
|
6365
|
+
const stats = await fs5.stat(filePath);
|
|
6366
|
+
return `${stats.mtimeMs}:${stats.size}`;
|
|
6367
|
+
} catch {
|
|
6368
|
+
return "missing";
|
|
6369
|
+
}
|
|
6370
|
+
}
|
|
5926
6371
|
};
|
|
5927
6372
|
defaultBridge = null;
|
|
5928
6373
|
}
|
|
@@ -5932,14 +6377,19 @@ ${profile.instructions}
|
|
|
5932
6377
|
function getAxCliMCPManager(options) {
|
|
5933
6378
|
if (!defaultManager2) {
|
|
5934
6379
|
defaultManager2 = new AxCliMCPManager(options);
|
|
6380
|
+
} else if (options) {
|
|
6381
|
+
logger.warn("AxCliMCPManager already initialized, ignoring new options", {
|
|
6382
|
+
hint: "Use resetAxCliMCPManager() to recreate with new options"
|
|
6383
|
+
});
|
|
5935
6384
|
}
|
|
5936
6385
|
return defaultManager2;
|
|
5937
6386
|
}
|
|
5938
|
-
var AxCliMCPManager, defaultManager2;
|
|
6387
|
+
var SDK_NOT_AVAILABLE_ERROR, AxCliMCPManager, defaultManager2;
|
|
5939
6388
|
var init_mcp_manager = __esm({
|
|
5940
6389
|
"src/integrations/ax-cli-sdk/mcp-manager.ts"() {
|
|
5941
6390
|
init_esm_shims();
|
|
5942
6391
|
init_logger();
|
|
6392
|
+
SDK_NOT_AVAILABLE_ERROR = "ax-cli SDK not available";
|
|
5943
6393
|
AxCliMCPManager = class {
|
|
5944
6394
|
sdkAvailable = null;
|
|
5945
6395
|
mcpModule = null;
|
|
@@ -5976,7 +6426,7 @@ var init_mcp_manager = __esm({
|
|
|
5976
6426
|
async getTemplateNames() {
|
|
5977
6427
|
try {
|
|
5978
6428
|
if (!await this.isAvailable()) {
|
|
5979
|
-
return { ok: false, error: new Error(
|
|
6429
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
5980
6430
|
}
|
|
5981
6431
|
const { MCPManager } = this.mcpModule;
|
|
5982
6432
|
if (MCPManager?.getTemplateNames) {
|
|
@@ -6013,7 +6463,7 @@ var init_mcp_manager = __esm({
|
|
|
6013
6463
|
async getTemplate(name) {
|
|
6014
6464
|
try {
|
|
6015
6465
|
if (!await this.isAvailable()) {
|
|
6016
|
-
return { ok: false, error: new Error(
|
|
6466
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6017
6467
|
}
|
|
6018
6468
|
const { MCPManager } = this.mcpModule;
|
|
6019
6469
|
if (MCPManager?.getTemplate) {
|
|
@@ -6037,7 +6487,7 @@ var init_mcp_manager = __esm({
|
|
|
6037
6487
|
async getTemplatesByCategory(category) {
|
|
6038
6488
|
try {
|
|
6039
6489
|
if (!await this.isAvailable()) {
|
|
6040
|
-
return { ok: false, error: new Error(
|
|
6490
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6041
6491
|
}
|
|
6042
6492
|
const { MCPManager } = this.mcpModule;
|
|
6043
6493
|
if (MCPManager?.getTemplatesByCategory) {
|
|
@@ -6058,7 +6508,7 @@ var init_mcp_manager = __esm({
|
|
|
6058
6508
|
async searchTemplates(keyword) {
|
|
6059
6509
|
try {
|
|
6060
6510
|
if (!await this.isAvailable()) {
|
|
6061
|
-
return { ok: false, error: new Error(
|
|
6511
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6062
6512
|
}
|
|
6063
6513
|
const { MCPManager } = this.mcpModule;
|
|
6064
6514
|
if (MCPManager?.searchTemplates) {
|
|
@@ -6079,7 +6529,7 @@ var init_mcp_manager = __esm({
|
|
|
6079
6529
|
async generateConfigFromTemplate(templateName, env) {
|
|
6080
6530
|
try {
|
|
6081
6531
|
if (!await this.isAvailable()) {
|
|
6082
|
-
return { ok: false, error: new Error(
|
|
6532
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6083
6533
|
}
|
|
6084
6534
|
const { MCPManager } = this.mcpModule;
|
|
6085
6535
|
if (MCPManager?.generateConfigFromTemplate) {
|
|
@@ -6100,7 +6550,7 @@ var init_mcp_manager = __esm({
|
|
|
6100
6550
|
async loadConfig() {
|
|
6101
6551
|
try {
|
|
6102
6552
|
if (!await this.isAvailable()) {
|
|
6103
|
-
return { ok: false, error: new Error(
|
|
6553
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6104
6554
|
}
|
|
6105
6555
|
const { MCPManager } = this.mcpModule;
|
|
6106
6556
|
if (MCPManager?.loadMCPConfig) {
|
|
@@ -6121,7 +6571,7 @@ var init_mcp_manager = __esm({
|
|
|
6121
6571
|
async addServer(name, config) {
|
|
6122
6572
|
try {
|
|
6123
6573
|
if (!await this.isAvailable()) {
|
|
6124
|
-
return { ok: false, error: new Error(
|
|
6574
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6125
6575
|
}
|
|
6126
6576
|
const { MCPManager } = this.mcpModule;
|
|
6127
6577
|
if (MCPManager?.addMCPServer) {
|
|
@@ -6143,7 +6593,7 @@ var init_mcp_manager = __esm({
|
|
|
6143
6593
|
async removeServer(name) {
|
|
6144
6594
|
try {
|
|
6145
6595
|
if (!await this.isAvailable()) {
|
|
6146
|
-
return { ok: false, error: new Error(
|
|
6596
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6147
6597
|
}
|
|
6148
6598
|
const { MCPManager } = this.mcpModule;
|
|
6149
6599
|
if (MCPManager?.removeMCPServer) {
|
|
@@ -6165,7 +6615,7 @@ var init_mcp_manager = __esm({
|
|
|
6165
6615
|
async getPredefinedServers() {
|
|
6166
6616
|
try {
|
|
6167
6617
|
if (!await this.isAvailable()) {
|
|
6168
|
-
return { ok: false, error: new Error(
|
|
6618
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6169
6619
|
}
|
|
6170
6620
|
const { MCPManager } = this.mcpModule;
|
|
6171
6621
|
if (MCPManager?.PREDEFINED_SERVERS) {
|
|
@@ -6217,7 +6667,7 @@ var init_mcp_manager = __esm({
|
|
|
6217
6667
|
async getServerStatus(name) {
|
|
6218
6668
|
try {
|
|
6219
6669
|
if (!await this.isAvailable()) {
|
|
6220
|
-
return { ok: false, error: new Error(
|
|
6670
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6221
6671
|
}
|
|
6222
6672
|
const { MCPManagerV2 } = this.mcpModule;
|
|
6223
6673
|
if (MCPManagerV2?.getServerStatus) {
|
|
@@ -6251,7 +6701,7 @@ var init_mcp_manager = __esm({
|
|
|
6251
6701
|
async getAllServerStatuses() {
|
|
6252
6702
|
try {
|
|
6253
6703
|
if (!await this.isAvailable()) {
|
|
6254
|
-
return { ok: false, error: new Error(
|
|
6704
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6255
6705
|
}
|
|
6256
6706
|
const { MCPManagerV2 } = this.mcpModule;
|
|
6257
6707
|
if (MCPManagerV2?.getAllServerStatuses) {
|
|
@@ -6275,7 +6725,7 @@ var init_mcp_manager = __esm({
|
|
|
6275
6725
|
async healthCheck() {
|
|
6276
6726
|
try {
|
|
6277
6727
|
if (!await this.isAvailable()) {
|
|
6278
|
-
return { ok: false, error: new Error(
|
|
6728
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6279
6729
|
}
|
|
6280
6730
|
const { MCPManagerV2 } = this.mcpModule;
|
|
6281
6731
|
if (MCPManagerV2?.healthCheck) {
|
|
@@ -6300,7 +6750,7 @@ var init_mcp_manager = __esm({
|
|
|
6300
6750
|
async startServer(name) {
|
|
6301
6751
|
try {
|
|
6302
6752
|
if (!await this.isAvailable()) {
|
|
6303
|
-
return { ok: false, error: new Error(
|
|
6753
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6304
6754
|
}
|
|
6305
6755
|
const { MCPManagerV2 } = this.mcpModule;
|
|
6306
6756
|
if (MCPManagerV2?.startServer) {
|
|
@@ -6315,9 +6765,16 @@ var init_mcp_manager = __esm({
|
|
|
6315
6765
|
}
|
|
6316
6766
|
return { ok: false, error: new Error("startServer not available in SDK") };
|
|
6317
6767
|
} catch (error) {
|
|
6768
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6769
|
+
this.serverStatuses.set(name, {
|
|
6770
|
+
name,
|
|
6771
|
+
status: "error",
|
|
6772
|
+
lastHealthCheck: /* @__PURE__ */ new Date(),
|
|
6773
|
+
error: errorMessage
|
|
6774
|
+
});
|
|
6318
6775
|
return {
|
|
6319
6776
|
ok: false,
|
|
6320
|
-
error: error instanceof Error ? error : new Error(
|
|
6777
|
+
error: error instanceof Error ? error : new Error(errorMessage)
|
|
6321
6778
|
};
|
|
6322
6779
|
}
|
|
6323
6780
|
}
|
|
@@ -6327,7 +6784,7 @@ var init_mcp_manager = __esm({
|
|
|
6327
6784
|
async stopServer(name) {
|
|
6328
6785
|
try {
|
|
6329
6786
|
if (!await this.isAvailable()) {
|
|
6330
|
-
return { ok: false, error: new Error(
|
|
6787
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6331
6788
|
}
|
|
6332
6789
|
const { MCPManagerV2 } = this.mcpModule;
|
|
6333
6790
|
if (MCPManagerV2?.stopServer) {
|
|
@@ -6342,9 +6799,16 @@ var init_mcp_manager = __esm({
|
|
|
6342
6799
|
}
|
|
6343
6800
|
return { ok: false, error: new Error("stopServer not available in SDK") };
|
|
6344
6801
|
} catch (error) {
|
|
6802
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6803
|
+
this.serverStatuses.set(name, {
|
|
6804
|
+
name,
|
|
6805
|
+
status: "error",
|
|
6806
|
+
lastHealthCheck: /* @__PURE__ */ new Date(),
|
|
6807
|
+
error: errorMessage
|
|
6808
|
+
});
|
|
6345
6809
|
return {
|
|
6346
6810
|
ok: false,
|
|
6347
|
-
error: error instanceof Error ? error : new Error(
|
|
6811
|
+
error: error instanceof Error ? error : new Error(errorMessage)
|
|
6348
6812
|
};
|
|
6349
6813
|
}
|
|
6350
6814
|
}
|
|
@@ -6354,7 +6818,7 @@ var init_mcp_manager = __esm({
|
|
|
6354
6818
|
async restartServer(name) {
|
|
6355
6819
|
try {
|
|
6356
6820
|
if (!await this.isAvailable()) {
|
|
6357
|
-
return { ok: false, error: new Error(
|
|
6821
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6358
6822
|
}
|
|
6359
6823
|
const { MCPManagerV2 } = this.mcpModule;
|
|
6360
6824
|
if (MCPManagerV2?.restartServer) {
|
|
@@ -6373,9 +6837,16 @@ var init_mcp_manager = __esm({
|
|
|
6373
6837
|
}
|
|
6374
6838
|
return this.startServer(name);
|
|
6375
6839
|
} catch (error) {
|
|
6840
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6841
|
+
this.serverStatuses.set(name, {
|
|
6842
|
+
name,
|
|
6843
|
+
status: "error",
|
|
6844
|
+
lastHealthCheck: /* @__PURE__ */ new Date(),
|
|
6845
|
+
error: errorMessage
|
|
6846
|
+
});
|
|
6376
6847
|
return {
|
|
6377
6848
|
ok: false,
|
|
6378
|
-
error: error instanceof Error ? error : new Error(
|
|
6849
|
+
error: error instanceof Error ? error : new Error(errorMessage)
|
|
6379
6850
|
};
|
|
6380
6851
|
}
|
|
6381
6852
|
}
|
|
@@ -6385,7 +6856,7 @@ var init_mcp_manager = __esm({
|
|
|
6385
6856
|
async shutdown() {
|
|
6386
6857
|
try {
|
|
6387
6858
|
if (!await this.isAvailable()) {
|
|
6388
|
-
return { ok: false, error: new Error(
|
|
6859
|
+
return { ok: false, error: new Error(SDK_NOT_AVAILABLE_ERROR) };
|
|
6389
6860
|
}
|
|
6390
6861
|
const { MCPManagerV2 } = this.mcpModule;
|
|
6391
6862
|
if (MCPManagerV2?.shutdown) {
|
|
@@ -6401,11 +6872,27 @@ var init_mcp_manager = __esm({
|
|
|
6401
6872
|
return { ok: true, value: void 0 };
|
|
6402
6873
|
}
|
|
6403
6874
|
const serversResult = await this.listServers();
|
|
6404
|
-
if (serversResult.ok) {
|
|
6405
|
-
|
|
6406
|
-
|
|
6875
|
+
if (!serversResult.ok) {
|
|
6876
|
+
logger.warn("Cannot enumerate servers for shutdown", {
|
|
6877
|
+
error: serversResult.error.message
|
|
6878
|
+
});
|
|
6879
|
+
return serversResult;
|
|
6880
|
+
}
|
|
6881
|
+
const errors = [];
|
|
6882
|
+
for (const name of serversResult.value) {
|
|
6883
|
+
const stopResult = await this.stopServer(name);
|
|
6884
|
+
if (!stopResult.ok) {
|
|
6885
|
+
errors.push({
|
|
6886
|
+
name,
|
|
6887
|
+
error: stopResult.error.message
|
|
6888
|
+
});
|
|
6407
6889
|
}
|
|
6408
6890
|
}
|
|
6891
|
+
if (errors.length > 0) {
|
|
6892
|
+
const errorMessage = `Failed to stop ${errors.length} server(s): ${errors.map((e) => `${e.name} (${e.error})`).join(", ")}`;
|
|
6893
|
+
logger.warn("Partial MCP shutdown failure", { errors });
|
|
6894
|
+
return { ok: false, error: new Error(errorMessage) };
|
|
6895
|
+
}
|
|
6409
6896
|
return { ok: true, value: void 0 };
|
|
6410
6897
|
} catch (error) {
|
|
6411
6898
|
return {
|
|
@@ -6420,7 +6907,7 @@ var init_mcp_manager = __esm({
|
|
|
6420
6907
|
});
|
|
6421
6908
|
|
|
6422
6909
|
// src/integrations/ax-cli-sdk/adapter.ts
|
|
6423
|
-
var AxCliSdkAdapter;
|
|
6910
|
+
var DEFAULT_MAX_TOOL_ROUNDS, AxCliSdkAdapter;
|
|
6424
6911
|
var init_adapter2 = __esm({
|
|
6425
6912
|
"src/integrations/ax-cli-sdk/adapter.ts"() {
|
|
6426
6913
|
init_esm_shims();
|
|
@@ -6431,10 +6918,13 @@ var init_adapter2 = __esm({
|
|
|
6431
6918
|
init_instructions_bridge();
|
|
6432
6919
|
init_mcp_manager();
|
|
6433
6920
|
init_mcp_manager();
|
|
6921
|
+
DEFAULT_MAX_TOOL_ROUNDS = 400;
|
|
6434
6922
|
AxCliSdkAdapter = class _AxCliSdkAdapter {
|
|
6435
6923
|
agent = null;
|
|
6436
6924
|
// Will type as LLMAgent after import
|
|
6437
6925
|
agentConfig = null;
|
|
6926
|
+
initPromise = null;
|
|
6927
|
+
executionLock = Promise.resolve();
|
|
6438
6928
|
reuseEnabled;
|
|
6439
6929
|
streamingEnabled;
|
|
6440
6930
|
sdkAvailable = null;
|
|
@@ -6457,6 +6947,9 @@ var init_adapter2 = __esm({
|
|
|
6457
6947
|
/**
|
|
6458
6948
|
* Get or create SubagentAdapter for parallel multi-agent execution
|
|
6459
6949
|
*
|
|
6950
|
+
* BUG FIX: Log warning when options are provided but adapter already exists,
|
|
6951
|
+
* as the options will be ignored.
|
|
6952
|
+
*
|
|
6460
6953
|
* @example
|
|
6461
6954
|
* ```typescript
|
|
6462
6955
|
* const subagents = adapter.getSubagentAdapter();
|
|
@@ -6470,6 +6963,10 @@ var init_adapter2 = __esm({
|
|
|
6470
6963
|
if (!this.subagentAdapter) {
|
|
6471
6964
|
this.subagentAdapter = new SubagentAdapter(options);
|
|
6472
6965
|
logger.debug("SubagentAdapter created");
|
|
6966
|
+
} else if (options) {
|
|
6967
|
+
logger.warn("SubagentAdapter already exists, ignoring new options", {
|
|
6968
|
+
hint: "Call destroy() first to recreate with new options"
|
|
6969
|
+
});
|
|
6473
6970
|
}
|
|
6474
6971
|
return this.subagentAdapter;
|
|
6475
6972
|
}
|
|
@@ -6495,6 +6992,9 @@ var init_adapter2 = __esm({
|
|
|
6495
6992
|
/**
|
|
6496
6993
|
* Get or create CheckpointAdapter for resumable workflows
|
|
6497
6994
|
*
|
|
6995
|
+
* BUG FIX: Log warning when options are provided but adapter already exists,
|
|
6996
|
+
* as the options will be ignored.
|
|
6997
|
+
*
|
|
6498
6998
|
* @example
|
|
6499
6999
|
* ```typescript
|
|
6500
7000
|
* const checkpoints = adapter.getCheckpointAdapter();
|
|
@@ -6506,6 +7006,10 @@ var init_adapter2 = __esm({
|
|
|
6506
7006
|
if (!this.checkpointAdapter) {
|
|
6507
7007
|
this.checkpointAdapter = new CheckpointAdapter(options);
|
|
6508
7008
|
logger.debug("CheckpointAdapter created");
|
|
7009
|
+
} else if (options) {
|
|
7010
|
+
logger.warn("CheckpointAdapter already exists, ignoring new options", {
|
|
7011
|
+
hint: "Call destroy() first to recreate with new options"
|
|
7012
|
+
});
|
|
6509
7013
|
}
|
|
6510
7014
|
return this.checkpointAdapter;
|
|
6511
7015
|
}
|
|
@@ -6545,6 +7049,9 @@ var init_adapter2 = __esm({
|
|
|
6545
7049
|
/**
|
|
6546
7050
|
* Get or create InstructionsBridge for unified agent instructions
|
|
6547
7051
|
*
|
|
7052
|
+
* BUG FIX: Log warning when options are provided but bridge already exists,
|
|
7053
|
+
* as the options will be ignored.
|
|
7054
|
+
*
|
|
6548
7055
|
* @example
|
|
6549
7056
|
* ```typescript
|
|
6550
7057
|
* const bridge = adapter.getInstructionsBridge();
|
|
@@ -6556,6 +7063,10 @@ var init_adapter2 = __esm({
|
|
|
6556
7063
|
if (!this.instructionsBridge) {
|
|
6557
7064
|
this.instructionsBridge = getInstructionsBridge(options);
|
|
6558
7065
|
logger.debug("InstructionsBridge created");
|
|
7066
|
+
} else if (options) {
|
|
7067
|
+
logger.warn("InstructionsBridge already exists, ignoring new options", {
|
|
7068
|
+
hint: "Call destroy() first to recreate with new options"
|
|
7069
|
+
});
|
|
6559
7070
|
}
|
|
6560
7071
|
return this.instructionsBridge;
|
|
6561
7072
|
}
|
|
@@ -6580,6 +7091,9 @@ var init_adapter2 = __esm({
|
|
|
6580
7091
|
/**
|
|
6581
7092
|
* Get or create MCP Manager for ax-cli MCP server management
|
|
6582
7093
|
*
|
|
7094
|
+
* BUG FIX: Log warning when options are provided but manager already exists,
|
|
7095
|
+
* as the options will be ignored.
|
|
7096
|
+
*
|
|
6583
7097
|
* @example
|
|
6584
7098
|
* ```typescript
|
|
6585
7099
|
* const mcp = adapter.getMCPManager();
|
|
@@ -6591,6 +7105,10 @@ var init_adapter2 = __esm({
|
|
|
6591
7105
|
if (!this.mcpManager) {
|
|
6592
7106
|
this.mcpManager = getAxCliMCPManager(options);
|
|
6593
7107
|
logger.debug("AxCliMCPManager created");
|
|
7108
|
+
} else if (options) {
|
|
7109
|
+
logger.warn("AxCliMCPManager already exists, ignoring new options", {
|
|
7110
|
+
hint: "Call destroy() first to recreate with new options"
|
|
7111
|
+
});
|
|
6594
7112
|
}
|
|
6595
7113
|
return this.mcpManager;
|
|
6596
7114
|
}
|
|
@@ -6636,21 +7154,35 @@ var init_adapter2 = __esm({
|
|
|
6636
7154
|
* Performance: ~5ms overhead vs ~50-200ms for CLI spawning
|
|
6637
7155
|
*/
|
|
6638
7156
|
async execute(prompt, options) {
|
|
7157
|
+
return this.runExclusive(() => this.executeInternal(prompt, options));
|
|
7158
|
+
}
|
|
7159
|
+
/**
|
|
7160
|
+
* Serialize executions to avoid chat history corruption and token mis-tracking
|
|
7161
|
+
* when the same adapter instance is used concurrently.
|
|
7162
|
+
*/
|
|
7163
|
+
async runExclusive(fn) {
|
|
7164
|
+
const resultPromise = this.executionLock.then(fn, fn);
|
|
7165
|
+
this.executionLock = resultPromise.then(
|
|
7166
|
+
() => Promise.resolve(),
|
|
7167
|
+
() => Promise.resolve()
|
|
7168
|
+
);
|
|
7169
|
+
return resultPromise;
|
|
7170
|
+
}
|
|
7171
|
+
async executeInternal(prompt, options) {
|
|
6639
7172
|
const startTime = Date.now();
|
|
6640
7173
|
let totalTokens = 0;
|
|
6641
7174
|
try {
|
|
6642
7175
|
if (!await this.ensureSDKAvailable()) {
|
|
6643
7176
|
throw new Error("ax-cli SDK not available. Install with: npm install @defai.digital/ax-cli");
|
|
6644
7177
|
}
|
|
6645
|
-
|
|
6646
|
-
await this.initializeAgent(options);
|
|
6647
|
-
}
|
|
7178
|
+
await this.ensureAgent(options);
|
|
6648
7179
|
logger.debug("Executing via SDK (streaming mode for token tracking)", {
|
|
6649
7180
|
promptLength: prompt.length,
|
|
6650
7181
|
userWantsCallbacks: !!(options.onStream || options.onTool),
|
|
6651
7182
|
note: "SDK handles custom instructions and project memory"
|
|
6652
7183
|
});
|
|
6653
|
-
const
|
|
7184
|
+
const chatHistory = typeof this.agent.getChatHistory === "function" ? this.agent.getChatHistory() : null;
|
|
7185
|
+
const historyLengthBefore = Array.isArray(chatHistory) ? chatHistory.length : 0;
|
|
6654
7186
|
for await (const chunk of this.agent.processUserMessageStream(prompt)) {
|
|
6655
7187
|
if (chunk.type === "token_count" && chunk.tokenCount) {
|
|
6656
7188
|
totalTokens += chunk.tokenCount;
|
|
@@ -6687,8 +7219,8 @@ var init_adapter2 = __esm({
|
|
|
6687
7219
|
}
|
|
6688
7220
|
}
|
|
6689
7221
|
}
|
|
6690
|
-
const fullHistory = this.agent.getChatHistory();
|
|
6691
|
-
const result = fullHistory.slice(historyLengthBefore);
|
|
7222
|
+
const fullHistory = typeof this.agent.getChatHistory === "function" ? this.agent.getChatHistory() : [];
|
|
7223
|
+
const result = Array.isArray(fullHistory) ? fullHistory.slice(historyLengthBefore) : [];
|
|
6692
7224
|
logger.debug("Chat history extraction", {
|
|
6693
7225
|
historyLengthBefore,
|
|
6694
7226
|
historyLengthAfter: fullHistory.length,
|
|
@@ -6728,10 +7260,11 @@ var init_adapter2 = __esm({
|
|
|
6728
7260
|
* We do NOT pass credentials to the SDK - it manages its own configuration.
|
|
6729
7261
|
*/
|
|
6730
7262
|
async initializeAgent(options) {
|
|
7263
|
+
await this.destroyAgentInstance();
|
|
6731
7264
|
const { createAgent } = await import('@defai.digital/ax-cli/sdk');
|
|
6732
7265
|
try {
|
|
6733
7266
|
const config = {
|
|
6734
|
-
maxToolRounds: options.maxToolRounds ||
|
|
7267
|
+
maxToolRounds: options.maxToolRounds || DEFAULT_MAX_TOOL_ROUNDS
|
|
6735
7268
|
};
|
|
6736
7269
|
logger.debug("Creating SDK agent (credentials from ax-cli settings)", {
|
|
6737
7270
|
maxToolRounds: config.maxToolRounds,
|
|
@@ -6793,22 +7326,75 @@ var init_adapter2 = __esm({
|
|
|
6793
7326
|
*/
|
|
6794
7327
|
hasConfigChanged(options) {
|
|
6795
7328
|
if (!this.agentConfig) return true;
|
|
6796
|
-
const changed = this.agentConfig.maxToolRounds !== (options.maxToolRounds ||
|
|
7329
|
+
const changed = this.agentConfig.maxToolRounds !== (options.maxToolRounds || DEFAULT_MAX_TOOL_ROUNDS);
|
|
6797
7330
|
if (changed) {
|
|
6798
7331
|
logger.debug("Agent config changed, will reinitialize", {
|
|
6799
7332
|
oldMaxToolRounds: this.agentConfig.maxToolRounds,
|
|
6800
|
-
newMaxToolRounds: options.maxToolRounds ||
|
|
7333
|
+
newMaxToolRounds: options.maxToolRounds || DEFAULT_MAX_TOOL_ROUNDS,
|
|
6801
7334
|
note: "SDK credentials managed by ax-cli setup"
|
|
6802
7335
|
});
|
|
6803
7336
|
}
|
|
6804
7337
|
return changed;
|
|
6805
7338
|
}
|
|
7339
|
+
/**
|
|
7340
|
+
* Ensure agent is initialized exactly once even if execute() is called
|
|
7341
|
+
* concurrently. Reinitializes when config changes.
|
|
7342
|
+
*
|
|
7343
|
+
* BUG FIX: If config changes while initialization is in progress, we need to
|
|
7344
|
+
* wait for current init to complete, then reinitialize with new config.
|
|
7345
|
+
*
|
|
7346
|
+
* BUG FIX (v11.3.3): Track initialization errors to prevent repeated init
|
|
7347
|
+
* attempts from creating a retry storm. If init fails, remember the error
|
|
7348
|
+
* and rethrow it for subsequent callers until reset.
|
|
7349
|
+
*/
|
|
7350
|
+
initError = null;
|
|
7351
|
+
async ensureAgent(options) {
|
|
7352
|
+
if (this.agent && !this.hasConfigChanged(options)) {
|
|
7353
|
+
return;
|
|
7354
|
+
}
|
|
7355
|
+
if (this.initError && !this.agent) {
|
|
7356
|
+
throw this.initError;
|
|
7357
|
+
}
|
|
7358
|
+
if (this.initPromise) {
|
|
7359
|
+
await this.initPromise;
|
|
7360
|
+
if (this.agent && !this.hasConfigChanged(options)) {
|
|
7361
|
+
return;
|
|
7362
|
+
}
|
|
7363
|
+
if (this.initError) {
|
|
7364
|
+
throw this.initError;
|
|
7365
|
+
}
|
|
7366
|
+
}
|
|
7367
|
+
this.initError = null;
|
|
7368
|
+
this.initPromise = this.initializeAgent(options).catch((error) => {
|
|
7369
|
+
this.initError = error instanceof Error ? error : new Error(String(error));
|
|
7370
|
+
throw error;
|
|
7371
|
+
}).finally(() => {
|
|
7372
|
+
this.initPromise = null;
|
|
7373
|
+
});
|
|
7374
|
+
await this.initPromise;
|
|
7375
|
+
}
|
|
6806
7376
|
/**
|
|
6807
7377
|
* Set up streaming event handlers (for initialization)
|
|
7378
|
+
*
|
|
7379
|
+
* BUG FIX (v11.3.3): Check if removeAllListeners exists before calling it.
|
|
7380
|
+
* Not all SDK agent implementations extend EventEmitter, and calling
|
|
7381
|
+
* a non-existent method would cause an error. Use optional chaining and
|
|
7382
|
+
* type guards to safely handle this.
|
|
6808
7383
|
*/
|
|
6809
7384
|
setupEventHandlers() {
|
|
6810
7385
|
if (!this.agent) return;
|
|
6811
7386
|
try {
|
|
7387
|
+
if (typeof this.agent.removeAllListeners === "function") {
|
|
7388
|
+
this.agent.removeAllListeners("stream");
|
|
7389
|
+
this.agent.removeAllListeners("tool");
|
|
7390
|
+
this.agent.removeAllListeners("error");
|
|
7391
|
+
} else if (typeof this.agent.off === "function") {
|
|
7392
|
+
logger.debug("Agent uses off() instead of removeAllListeners(), skipping listener cleanup");
|
|
7393
|
+
}
|
|
7394
|
+
if (typeof this.agent.on !== "function") {
|
|
7395
|
+
logger.debug("Agent does not support event subscription, skipping event handlers");
|
|
7396
|
+
return;
|
|
7397
|
+
}
|
|
6812
7398
|
this.agent.on("stream", (chunk) => {
|
|
6813
7399
|
logger.debug("Stream chunk received", {
|
|
6814
7400
|
type: chunk.type,
|
|
@@ -6831,50 +7417,6 @@ var init_adapter2 = __esm({
|
|
|
6831
7417
|
});
|
|
6832
7418
|
}
|
|
6833
7419
|
}
|
|
6834
|
-
/**
|
|
6835
|
-
* Set up streaming callbacks for execution
|
|
6836
|
-
*/
|
|
6837
|
-
setupStreamingCallbacks(options) {
|
|
6838
|
-
if (!this.agent) return;
|
|
6839
|
-
try {
|
|
6840
|
-
this.agent.removeAllListeners("stream");
|
|
6841
|
-
this.agent.removeAllListeners("tool");
|
|
6842
|
-
if (options.onStream) {
|
|
6843
|
-
this.agent.on("stream", (chunk) => {
|
|
6844
|
-
try {
|
|
6845
|
-
options.onStream({
|
|
6846
|
-
type: chunk.type || "content",
|
|
6847
|
-
content: chunk.content,
|
|
6848
|
-
tool: chunk.tool,
|
|
6849
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6850
|
-
});
|
|
6851
|
-
} catch (error) {
|
|
6852
|
-
logger.warn("Stream callback error", {
|
|
6853
|
-
error: error instanceof Error ? error.message : String(error)
|
|
6854
|
-
});
|
|
6855
|
-
}
|
|
6856
|
-
});
|
|
6857
|
-
}
|
|
6858
|
-
if (options.onTool) {
|
|
6859
|
-
this.agent.on("tool", (data) => {
|
|
6860
|
-
try {
|
|
6861
|
-
options.onTool({
|
|
6862
|
-
name: data.name,
|
|
6863
|
-
args: data.args
|
|
6864
|
-
});
|
|
6865
|
-
} catch (error) {
|
|
6866
|
-
logger.warn("Tool callback error", {
|
|
6867
|
-
error: error instanceof Error ? error.message : String(error)
|
|
6868
|
-
});
|
|
6869
|
-
}
|
|
6870
|
-
});
|
|
6871
|
-
}
|
|
6872
|
-
} catch (error) {
|
|
6873
|
-
logger.warn("Failed to setup streaming callbacks", {
|
|
6874
|
-
error: error instanceof Error ? error.message : String(error)
|
|
6875
|
-
});
|
|
6876
|
-
}
|
|
6877
|
-
}
|
|
6878
7420
|
/**
|
|
6879
7421
|
* Extract token usage from various sources with priority:
|
|
6880
7422
|
* 1. SDK events (token_count emissions) - most accurate
|
|
@@ -6898,9 +7440,9 @@ var init_adapter2 = __esm({
|
|
|
6898
7440
|
}
|
|
6899
7441
|
const usage = usageObject || {};
|
|
6900
7442
|
const actualTokens = {
|
|
6901
|
-
prompt: usage.prompt_tokens
|
|
6902
|
-
completion: usage.completion_tokens
|
|
6903
|
-
total: usage.total_tokens
|
|
7443
|
+
prompt: usage.prompt_tokens ?? usage.prompt ?? usage.input ?? usage.promptTokens ?? 0,
|
|
7444
|
+
completion: usage.completion_tokens ?? usage.completion ?? usage.output ?? usage.completionTokens ?? 0,
|
|
7445
|
+
total: usage.total_tokens ?? usage.total ?? usage.totalTokens ?? 0
|
|
6904
7446
|
};
|
|
6905
7447
|
if (actualTokens.total > 0) {
|
|
6906
7448
|
logger.info(`Using token counts from ${sourceName || "usage object"}`, {
|
|
@@ -6964,12 +7506,12 @@ var init_adapter2 = __esm({
|
|
|
6964
7506
|
cached: false
|
|
6965
7507
|
};
|
|
6966
7508
|
}
|
|
6967
|
-
const content = typeof result === "string" ? result : result
|
|
7509
|
+
const content = typeof result === "string" ? result : result?.content || result?.text || "";
|
|
6968
7510
|
const tokensUsed = this.extractTokenUsage(
|
|
6969
7511
|
prompt,
|
|
6970
7512
|
content,
|
|
6971
7513
|
totalTokensFromEvents,
|
|
6972
|
-
result
|
|
7514
|
+
result?.usage || result?.tokens,
|
|
6973
7515
|
"response.usage (fallback path)"
|
|
6974
7516
|
);
|
|
6975
7517
|
return {
|
|
@@ -7058,21 +7600,7 @@ var init_adapter2 = __esm({
|
|
|
7058
7600
|
* Cleanup resources
|
|
7059
7601
|
*/
|
|
7060
7602
|
async destroy() {
|
|
7061
|
-
|
|
7062
|
-
try {
|
|
7063
|
-
const result = this.agent.dispose();
|
|
7064
|
-
if (result instanceof Promise) {
|
|
7065
|
-
await result;
|
|
7066
|
-
}
|
|
7067
|
-
this.agent = null;
|
|
7068
|
-
this.agentConfig = null;
|
|
7069
|
-
logger.debug("SDK agent destroyed");
|
|
7070
|
-
} catch (error) {
|
|
7071
|
-
logger.warn("Error during agent cleanup", {
|
|
7072
|
-
error: error instanceof Error ? error.message : String(error)
|
|
7073
|
-
});
|
|
7074
|
-
}
|
|
7075
|
-
}
|
|
7603
|
+
await this.destroyAgentInstance();
|
|
7076
7604
|
if (this.subagentAdapter) {
|
|
7077
7605
|
try {
|
|
7078
7606
|
await this.subagentAdapter.destroy();
|
|
@@ -7110,8 +7638,43 @@ var init_adapter2 = __esm({
|
|
|
7110
7638
|
}
|
|
7111
7639
|
}
|
|
7112
7640
|
if (this.mcpManager) {
|
|
7113
|
-
|
|
7114
|
-
|
|
7641
|
+
try {
|
|
7642
|
+
const shutdownResult = await this.mcpManager.shutdown();
|
|
7643
|
+
if (!shutdownResult.ok) {
|
|
7644
|
+
logger.warn("Error while shutting down MCP servers", {
|
|
7645
|
+
error: shutdownResult.error instanceof Error ? shutdownResult.error.message : String(shutdownResult.error)
|
|
7646
|
+
});
|
|
7647
|
+
}
|
|
7648
|
+
} catch (error) {
|
|
7649
|
+
logger.warn("Error during MCP Manager cleanup", {
|
|
7650
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7651
|
+
});
|
|
7652
|
+
} finally {
|
|
7653
|
+
this.mcpManager = null;
|
|
7654
|
+
logger.debug("MCP Manager cleared");
|
|
7655
|
+
}
|
|
7656
|
+
}
|
|
7657
|
+
}
|
|
7658
|
+
/**
|
|
7659
|
+
* Dispose the primary agent without touching ancillary adapters.
|
|
7660
|
+
* BUG FIX (v11.3.3): Also clear initError to allow retry after destroy.
|
|
7661
|
+
*/
|
|
7662
|
+
async destroyAgentInstance() {
|
|
7663
|
+
this.initError = null;
|
|
7664
|
+
if (!this.agent) return;
|
|
7665
|
+
try {
|
|
7666
|
+
const result = this.agent.dispose();
|
|
7667
|
+
if (result instanceof Promise) {
|
|
7668
|
+
await result;
|
|
7669
|
+
}
|
|
7670
|
+
} catch (error) {
|
|
7671
|
+
logger.warn("Error during agent cleanup", {
|
|
7672
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7673
|
+
});
|
|
7674
|
+
} finally {
|
|
7675
|
+
this.agent = null;
|
|
7676
|
+
this.agentConfig = null;
|
|
7677
|
+
logger.debug("SDK agent destroyed");
|
|
7115
7678
|
}
|
|
7116
7679
|
}
|
|
7117
7680
|
};
|
|
@@ -7158,10 +7721,22 @@ var init_hybrid_adapter2 = __esm({
|
|
|
7158
7721
|
}
|
|
7159
7722
|
throw new Error(`No adapter available for mode: ${this.mode}`);
|
|
7160
7723
|
} catch (error) {
|
|
7161
|
-
if (this.mode === "auto" && this.activeMode === "sdk"
|
|
7724
|
+
if (this.mode === "auto" && this.activeMode === "sdk") {
|
|
7162
7725
|
logger.warn("SDK execution failed, falling back to CLI", {
|
|
7163
7726
|
error: error instanceof Error ? error.message : String(error)
|
|
7164
7727
|
});
|
|
7728
|
+
if (this.sdkAdapter) {
|
|
7729
|
+
const sdkAdapterToClean = this.sdkAdapter;
|
|
7730
|
+
this.sdkAdapter = null;
|
|
7731
|
+
sdkAdapterToClean.destroy().catch((cleanupError) => {
|
|
7732
|
+
logger.warn("Error during SDK adapter cleanup on fallback", {
|
|
7733
|
+
error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
|
|
7734
|
+
});
|
|
7735
|
+
});
|
|
7736
|
+
}
|
|
7737
|
+
if (!this.cliAdapter) {
|
|
7738
|
+
this.initializeCli();
|
|
7739
|
+
}
|
|
7165
7740
|
this.activeMode = "cli";
|
|
7166
7741
|
return await this.executeCli(prompt, options);
|
|
7167
7742
|
}
|
|
@@ -7230,15 +7805,25 @@ var init_hybrid_adapter2 = __esm({
|
|
|
7230
7805
|
*
|
|
7231
7806
|
* @param required - If true, throws if SDK unavailable
|
|
7232
7807
|
* @returns true if SDK is available, false otherwise
|
|
7808
|
+
*
|
|
7809
|
+
* BUG FIX (v11.3.3): Clean up SDK adapter if isAvailable() returns false.
|
|
7810
|
+
* Previously the adapter was just set to null without calling destroy(),
|
|
7811
|
+
* which could leak resources initialized in the constructor.
|
|
7233
7812
|
*/
|
|
7234
7813
|
async initializeSdk(required) {
|
|
7235
7814
|
if (this.sdkAdapter) {
|
|
7236
7815
|
return true;
|
|
7237
7816
|
}
|
|
7817
|
+
let adapter = null;
|
|
7238
7818
|
try {
|
|
7239
|
-
|
|
7240
|
-
const available = await
|
|
7819
|
+
adapter = new AxCliSdkAdapter(this.sdkOptions);
|
|
7820
|
+
const available = await adapter.isAvailable();
|
|
7241
7821
|
if (!available) {
|
|
7822
|
+
await adapter.destroy().catch((cleanupError) => {
|
|
7823
|
+
logger.warn("Error cleaning up unavailable SDK adapter", {
|
|
7824
|
+
error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
|
|
7825
|
+
});
|
|
7826
|
+
});
|
|
7242
7827
|
if (required) {
|
|
7243
7828
|
throw new Error(
|
|
7244
7829
|
"ax-cli SDK not available. Install with: npm install @defai.digital/ax-cli"
|
|
@@ -7247,14 +7832,21 @@ var init_hybrid_adapter2 = __esm({
|
|
|
7247
7832
|
logger.warn("SDK not available", {
|
|
7248
7833
|
hint: "Install with: npm install @defai.digital/ax-cli"
|
|
7249
7834
|
});
|
|
7250
|
-
this.sdkAdapter = null;
|
|
7251
7835
|
return false;
|
|
7252
7836
|
}
|
|
7837
|
+
this.sdkAdapter = adapter;
|
|
7253
7838
|
logger.info("SDK adapter initialized", {
|
|
7254
7839
|
version: await this.sdkAdapter.getVersion()
|
|
7255
7840
|
});
|
|
7256
7841
|
return true;
|
|
7257
7842
|
} catch (error) {
|
|
7843
|
+
if (adapter) {
|
|
7844
|
+
await adapter.destroy().catch((cleanupError) => {
|
|
7845
|
+
logger.warn("Error cleaning up SDK adapter after error", {
|
|
7846
|
+
error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
|
|
7847
|
+
});
|
|
7848
|
+
});
|
|
7849
|
+
}
|
|
7258
7850
|
logger.error("Failed to initialize SDK adapter", {
|
|
7259
7851
|
error: error instanceof Error ? error.message : String(error)
|
|
7260
7852
|
});
|
|
@@ -7303,6 +7895,9 @@ var init_hybrid_adapter2 = __esm({
|
|
|
7303
7895
|
}
|
|
7304
7896
|
/**
|
|
7305
7897
|
* Get command name
|
|
7898
|
+
*
|
|
7899
|
+
* BUG FIX: Handle 'auto' mode correctly - return 'ax-cli-auto' to indicate
|
|
7900
|
+
* the actual command hasn't been determined yet (will be sdk or cli after initialization)
|
|
7306
7901
|
*/
|
|
7307
7902
|
getCommand() {
|
|
7308
7903
|
if (this.activeMode === "sdk") {
|
|
@@ -7310,7 +7905,13 @@ var init_hybrid_adapter2 = __esm({
|
|
|
7310
7905
|
} else if (this.activeMode === "cli") {
|
|
7311
7906
|
return "ax-cli";
|
|
7312
7907
|
}
|
|
7313
|
-
|
|
7908
|
+
if (this.mode === "sdk") {
|
|
7909
|
+
return "ax-cli-sdk";
|
|
7910
|
+
} else if (this.mode === "cli") {
|
|
7911
|
+
return "ax-cli";
|
|
7912
|
+
} else {
|
|
7913
|
+
return "ax-cli-auto";
|
|
7914
|
+
}
|
|
7314
7915
|
}
|
|
7315
7916
|
/**
|
|
7316
7917
|
* Get display name
|
|
@@ -7331,9 +7932,22 @@ var init_hybrid_adapter2 = __esm({
|
|
|
7331
7932
|
}
|
|
7332
7933
|
/**
|
|
7333
7934
|
* Force switch to CLI mode (for debugging or fallback)
|
|
7935
|
+
*
|
|
7936
|
+
* BUG FIX: Destroy SDK adapter when switching to CLI to prevent resource leaks
|
|
7937
|
+
* (memory, event listeners) from the unused SDK adapter.
|
|
7334
7938
|
*/
|
|
7335
|
-
switchToCliMode() {
|
|
7939
|
+
async switchToCliMode() {
|
|
7336
7940
|
logger.info("Forcing switch to CLI mode");
|
|
7941
|
+
if (this.sdkAdapter) {
|
|
7942
|
+
try {
|
|
7943
|
+
await this.sdkAdapter.destroy();
|
|
7944
|
+
} catch (error) {
|
|
7945
|
+
logger.warn("Error destroying SDK adapter during mode switch", {
|
|
7946
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7947
|
+
});
|
|
7948
|
+
}
|
|
7949
|
+
this.sdkAdapter = null;
|
|
7950
|
+
}
|
|
7337
7951
|
this.initializeCli();
|
|
7338
7952
|
this.activeMode = "cli";
|
|
7339
7953
|
}
|
|
@@ -7353,13 +7967,22 @@ var init_hybrid_adapter2 = __esm({
|
|
|
7353
7967
|
}
|
|
7354
7968
|
/**
|
|
7355
7969
|
* Cleanup resources
|
|
7970
|
+
* BUG FIX: Use Promise.allSettled to ensure cleanup completes even if SDK destroy fails
|
|
7356
7971
|
*/
|
|
7357
7972
|
async destroy() {
|
|
7358
7973
|
const destroyPromises = [];
|
|
7359
7974
|
if (this.sdkAdapter) {
|
|
7360
7975
|
destroyPromises.push(this.sdkAdapter.destroy());
|
|
7361
7976
|
}
|
|
7362
|
-
await Promise.
|
|
7977
|
+
const results = await Promise.allSettled(destroyPromises);
|
|
7978
|
+
results.forEach((result, index) => {
|
|
7979
|
+
if (result.status === "rejected") {
|
|
7980
|
+
logger.warn("Adapter cleanup failed", {
|
|
7981
|
+
index,
|
|
7982
|
+
error: result.reason instanceof Error ? result.reason.message : String(result.reason)
|
|
7983
|
+
});
|
|
7984
|
+
}
|
|
7985
|
+
});
|
|
7363
7986
|
this.sdkAdapter = null;
|
|
7364
7987
|
this.cliAdapter = null;
|
|
7365
7988
|
this.activeMode = null;
|
|
@@ -7375,7 +7998,7 @@ __export(ax_cli_provider_exports, {
|
|
|
7375
7998
|
AxCliProvider: () => AxCliProvider,
|
|
7376
7999
|
GlmProvider: () => GlmProvider
|
|
7377
8000
|
});
|
|
7378
|
-
var SDK_ADAPTER_UNAVAILABLE_ERROR, AxCliProvider, GlmProvider;
|
|
8001
|
+
var SDK_ADAPTER_UNAVAILABLE_ERROR, DEFAULT_MAX_TOOL_ROUNDS2, AxCliProvider, GlmProvider;
|
|
7379
8002
|
var init_ax_cli_provider = __esm({
|
|
7380
8003
|
"src/providers/ax-cli-provider.ts"() {
|
|
7381
8004
|
init_esm_shims();
|
|
@@ -7383,6 +8006,7 @@ var init_ax_cli_provider = __esm({
|
|
|
7383
8006
|
init_hybrid_adapter2();
|
|
7384
8007
|
init_logger();
|
|
7385
8008
|
SDK_ADAPTER_UNAVAILABLE_ERROR = 'SDK adapter not available. Ensure mode="sdk" or "auto" with SDK installed.';
|
|
8009
|
+
DEFAULT_MAX_TOOL_ROUNDS2 = 400;
|
|
7386
8010
|
AxCliProvider = class extends BaseProvider {
|
|
7387
8011
|
adapter;
|
|
7388
8012
|
config;
|
|
@@ -7413,7 +8037,7 @@ var init_ax_cli_provider = __esm({
|
|
|
7413
8037
|
const options = {
|
|
7414
8038
|
model: request.model || axCliConfig.model,
|
|
7415
8039
|
// Optional model override
|
|
7416
|
-
maxToolRounds: axCliConfig.maxToolRounds ||
|
|
8040
|
+
maxToolRounds: axCliConfig.maxToolRounds || DEFAULT_MAX_TOOL_ROUNDS2,
|
|
7417
8041
|
timeout: this.config.timeout,
|
|
7418
8042
|
// Note: apiKey and baseUrl are ignored by SDK adapter (v3.7.0+)
|
|
7419
8043
|
// SDK loads credentials from ax-cli setup (~/.ax-cli/config.json)
|
|
@@ -7441,18 +8065,31 @@ var init_ax_cli_provider = __esm({
|
|
|
7441
8065
|
error: error instanceof Error ? error.message : String(error),
|
|
7442
8066
|
model: options.model || "(from ax-cli setup)"
|
|
7443
8067
|
});
|
|
7444
|
-
throw error;
|
|
8068
|
+
throw this.handleError(error);
|
|
7445
8069
|
}
|
|
7446
8070
|
}
|
|
7447
8071
|
/**
|
|
7448
8072
|
* Check if ax-cli is available
|
|
7449
8073
|
*
|
|
8074
|
+
* BUG FIX: Update health status like BaseProvider.isAvailable() does.
|
|
8075
|
+
* Previously only returned the availability boolean without updating
|
|
8076
|
+
* the provider's health state, which meant consecutiveFailures,
|
|
8077
|
+
* consecutiveSuccesses, and errorRate weren't tracked for this provider.
|
|
8078
|
+
*
|
|
7450
8079
|
* @returns True if ax-cli is installed and accessible
|
|
7451
8080
|
*/
|
|
7452
8081
|
async isAvailable() {
|
|
7453
8082
|
try {
|
|
7454
8083
|
const available = await this.adapter.isAvailable();
|
|
7455
|
-
|
|
8084
|
+
this.health.available = available;
|
|
8085
|
+
this.health.errorRate = available ? 0 : 1;
|
|
8086
|
+
this.health.lastCheck = Date.now();
|
|
8087
|
+
if (available) {
|
|
8088
|
+
this.health.consecutiveFailures = 0;
|
|
8089
|
+
this.health.consecutiveSuccesses++;
|
|
8090
|
+
} else {
|
|
8091
|
+
this.health.consecutiveFailures++;
|
|
8092
|
+
this.health.consecutiveSuccesses = 0;
|
|
7456
8093
|
logger.warn("ax-cli is not available", {
|
|
7457
8094
|
provider: this.name,
|
|
7458
8095
|
command: this.adapter.getCommand()
|
|
@@ -7460,6 +8097,11 @@ var init_ax_cli_provider = __esm({
|
|
|
7460
8097
|
}
|
|
7461
8098
|
return available;
|
|
7462
8099
|
} catch (error) {
|
|
8100
|
+
this.health.available = false;
|
|
8101
|
+
this.health.errorRate = 1;
|
|
8102
|
+
this.health.lastCheck = Date.now();
|
|
8103
|
+
this.health.consecutiveFailures++;
|
|
8104
|
+
this.health.consecutiveSuccesses = 0;
|
|
7463
8105
|
logger.error("Failed to check ax-cli availability", {
|
|
7464
8106
|
error: error instanceof Error ? error.message : String(error)
|
|
7465
8107
|
});
|
|
@@ -7499,9 +8141,11 @@ var init_ax_cli_provider = __esm({
|
|
|
7499
8141
|
}
|
|
7500
8142
|
/**
|
|
7501
8143
|
* Force switch to CLI mode (for debugging or fallback)
|
|
8144
|
+
*
|
|
8145
|
+
* This method is async because it needs to clean up SDK adapter resources.
|
|
7502
8146
|
*/
|
|
7503
|
-
switchToCliMode() {
|
|
7504
|
-
this.adapter.switchToCliMode();
|
|
8147
|
+
async switchToCliMode() {
|
|
8148
|
+
await this.adapter.switchToCliMode();
|
|
7505
8149
|
}
|
|
7506
8150
|
// ==========================================
|
|
7507
8151
|
// SDK Advanced Features (v10.4.0)
|
|
@@ -7520,7 +8164,7 @@ var init_ax_cli_provider = __esm({
|
|
|
7520
8164
|
* ]);
|
|
7521
8165
|
* ```
|
|
7522
8166
|
*/
|
|
7523
|
-
async executeParallelTasks(tasks
|
|
8167
|
+
async executeParallelTasks(tasks) {
|
|
7524
8168
|
const sdkAdapter = this.adapter.getSdkAdapter();
|
|
7525
8169
|
if (!sdkAdapter) {
|
|
7526
8170
|
throw new Error(SDK_ADAPTER_UNAVAILABLE_ERROR);
|
|
@@ -7649,6 +8293,28 @@ var init_ax_cli_provider = __esm({
|
|
|
7649
8293
|
getMockResponse() {
|
|
7650
8294
|
return "Mock ax-cli response for testing";
|
|
7651
8295
|
}
|
|
8296
|
+
/**
|
|
8297
|
+
* Cleanup provider resources
|
|
8298
|
+
*
|
|
8299
|
+
* BUG FIX (v11.3.3): AxCliProvider was missing a destroy() method, which meant
|
|
8300
|
+
* the underlying HybridAxCliAdapter (and its SDK adapter, subagent adapter,
|
|
8301
|
+
* checkpoint adapter, etc.) would never be cleaned up. This could cause:
|
|
8302
|
+
* - Memory leaks from cached SDK agents
|
|
8303
|
+
* - Unflushed checkpoint data
|
|
8304
|
+
* - Orphaned MCP server connections
|
|
8305
|
+
*
|
|
8306
|
+
* Call this method when the provider is no longer needed.
|
|
8307
|
+
*/
|
|
8308
|
+
async destroy() {
|
|
8309
|
+
try {
|
|
8310
|
+
await this.adapter.destroy();
|
|
8311
|
+
logger.info("AxCliProvider destroyed");
|
|
8312
|
+
} catch (error) {
|
|
8313
|
+
logger.warn("Error during AxCliProvider cleanup", {
|
|
8314
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8315
|
+
});
|
|
8316
|
+
}
|
|
8317
|
+
}
|
|
7652
8318
|
};
|
|
7653
8319
|
GlmProvider = AxCliProvider;
|
|
7654
8320
|
}
|
|
@@ -8626,11 +9292,11 @@ var VALIDATION_LIMITS = {
|
|
|
8626
9292
|
MAX_PORT: 65535
|
|
8627
9293
|
// Maximum port number
|
|
8628
9294
|
};
|
|
8629
|
-
function isValidRelativePath(
|
|
8630
|
-
if (!
|
|
9295
|
+
function isValidRelativePath(path8) {
|
|
9296
|
+
if (!path8 || typeof path8 !== "string") {
|
|
8631
9297
|
return false;
|
|
8632
9298
|
}
|
|
8633
|
-
const normalizedPath =
|
|
9299
|
+
const normalizedPath = path8.replace(/\\/g, "/");
|
|
8634
9300
|
if (normalizedPath.startsWith("/")) {
|
|
8635
9301
|
return false;
|
|
8636
9302
|
}
|
|
@@ -9022,7 +9688,7 @@ var PRECOMPILED_CONFIG = {
|
|
|
9022
9688
|
"providers": {
|
|
9023
9689
|
"claude-code": {
|
|
9024
9690
|
"enabled": true,
|
|
9025
|
-
"priority":
|
|
9691
|
+
"priority": 3,
|
|
9026
9692
|
"timeout": 27e5,
|
|
9027
9693
|
"command": "claude",
|
|
9028
9694
|
"healthCheck": {
|
|
@@ -9082,7 +9748,7 @@ var PRECOMPILED_CONFIG = {
|
|
|
9082
9748
|
},
|
|
9083
9749
|
"openai": {
|
|
9084
9750
|
"enabled": true,
|
|
9085
|
-
"priority":
|
|
9751
|
+
"priority": 1,
|
|
9086
9752
|
"timeout": 27e5,
|
|
9087
9753
|
"command": "codex",
|
|
9088
9754
|
"healthCheck": {
|
|
@@ -9320,7 +9986,7 @@ var PRECOMPILED_CONFIG = {
|
|
|
9320
9986
|
"enableFreeTierPrioritization": true,
|
|
9321
9987
|
"enableWorkloadAwareRouting": true
|
|
9322
9988
|
},
|
|
9323
|
-
"version": "11.3.
|
|
9989
|
+
"version": "11.3.3"
|
|
9324
9990
|
};
|
|
9325
9991
|
|
|
9326
9992
|
// src/core/config/schemas.ts
|
|
@@ -9347,7 +10013,7 @@ var safeNameSchema = z.string().min(1).max(VALIDATION_LIMITS.MAX_NAME_LENGTH).re
|
|
|
9347
10013
|
"Name must be alphanumeric with dash/underscore only"
|
|
9348
10014
|
).describe("Safe identifier name");
|
|
9349
10015
|
var relativePathSchema = z.string().min(1).refine(
|
|
9350
|
-
(
|
|
10016
|
+
(path8) => !path8.includes("..") && !path8.startsWith("/"),
|
|
9351
10017
|
"Path must be relative (no ../, no absolute paths)"
|
|
9352
10018
|
).describe("Relative path within project");
|
|
9353
10019
|
var fileExtensionSchema = z.string().regex(
|
|
@@ -9687,16 +10353,16 @@ async function loadConfigUncached(projectDir) {
|
|
|
9687
10353
|
});
|
|
9688
10354
|
return config;
|
|
9689
10355
|
}
|
|
9690
|
-
async function loadConfigFile(
|
|
10356
|
+
async function loadConfigFile(path8) {
|
|
9691
10357
|
try {
|
|
9692
|
-
const content = await readFile(
|
|
10358
|
+
const content = await readFile(path8, "utf-8");
|
|
9693
10359
|
if (content.length > VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE) {
|
|
9694
10360
|
throw ConfigError.parseError(
|
|
9695
10361
|
new Error(`Config file too large (max ${VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE / 1024}KB, got ${Math.ceil(content.length / 1024)}KB)`),
|
|
9696
|
-
|
|
10362
|
+
path8
|
|
9697
10363
|
);
|
|
9698
10364
|
}
|
|
9699
|
-
const ext = extname(
|
|
10365
|
+
const ext = extname(path8).toLowerCase();
|
|
9700
10366
|
let userConfig;
|
|
9701
10367
|
try {
|
|
9702
10368
|
if (ext === ".yaml" || ext === ".yml") {
|
|
@@ -9705,7 +10371,7 @@ async function loadConfigFile(path7) {
|
|
|
9705
10371
|
userConfig = JSON.parse(content);
|
|
9706
10372
|
}
|
|
9707
10373
|
} catch (parseError) {
|
|
9708
|
-
throw ConfigError.parseError(parseError,
|
|
10374
|
+
throw ConfigError.parseError(parseError, path8);
|
|
9709
10375
|
}
|
|
9710
10376
|
const config = mergeConfig(DEFAULT_CONFIG, userConfig);
|
|
9711
10377
|
if (config.execution && userConfig.execution?.maxConcurrentAgents === void 0) {
|
|
@@ -9721,35 +10387,35 @@ async function loadConfigFile(path7) {
|
|
|
9721
10387
|
if (validationErrors.length > 0) {
|
|
9722
10388
|
throw ConfigError.invalid(
|
|
9723
10389
|
validationErrors.join("; "),
|
|
9724
|
-
{ path:
|
|
10390
|
+
{ path: path8, errors: validationErrors }
|
|
9725
10391
|
);
|
|
9726
10392
|
}
|
|
9727
|
-
logger.info("Config loaded successfully", { path: normalizePath(
|
|
10393
|
+
logger.info("Config loaded successfully", { path: normalizePath(path8), format: ext });
|
|
9728
10394
|
return config;
|
|
9729
10395
|
} catch (error) {
|
|
9730
10396
|
if (error instanceof ConfigError) {
|
|
9731
10397
|
throw error;
|
|
9732
10398
|
}
|
|
9733
10399
|
if (error.code === "ENOENT") {
|
|
9734
|
-
throw ConfigError.notFound(
|
|
10400
|
+
throw ConfigError.notFound(path8);
|
|
9735
10401
|
}
|
|
9736
10402
|
if (error.code === "EACCES") {
|
|
9737
10403
|
throw new ConfigError(
|
|
9738
|
-
`Permission denied reading config: ${
|
|
10404
|
+
`Permission denied reading config: ${path8}`,
|
|
9739
10405
|
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
9740
10406
|
[
|
|
9741
10407
|
"Check file permissions",
|
|
9742
10408
|
"Run with appropriate user privileges",
|
|
9743
10409
|
"Verify the file is accessible"
|
|
9744
10410
|
],
|
|
9745
|
-
{ path:
|
|
10411
|
+
{ path: path8, error: error.message }
|
|
9746
10412
|
);
|
|
9747
10413
|
}
|
|
9748
10414
|
throw new ConfigError(
|
|
9749
10415
|
`Failed to load config: ${error.message}`,
|
|
9750
10416
|
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
9751
10417
|
["Check file format and permissions"],
|
|
9752
|
-
{ path:
|
|
10418
|
+
{ path: path8, originalError: error.message }
|
|
9753
10419
|
);
|
|
9754
10420
|
}
|
|
9755
10421
|
}
|
|
@@ -9765,8 +10431,8 @@ function validateConfigWithZod(config) {
|
|
|
9765
10431
|
return ["Configuration validation failed with unknown error structure"];
|
|
9766
10432
|
}
|
|
9767
10433
|
return result.error.issues.map((err) => {
|
|
9768
|
-
const
|
|
9769
|
-
return `${
|
|
10434
|
+
const path8 = err.path.join(".");
|
|
10435
|
+
return `${path8}: ${err.message}`;
|
|
9770
10436
|
});
|
|
9771
10437
|
}
|
|
9772
10438
|
function validateConfig(config) {
|
|
@@ -10152,16 +10818,16 @@ function validateConfig(config) {
|
|
|
10152
10818
|
}
|
|
10153
10819
|
return errors;
|
|
10154
10820
|
}
|
|
10155
|
-
async function saveConfigFile(
|
|
10821
|
+
async function saveConfigFile(path8, config) {
|
|
10156
10822
|
try {
|
|
10157
10823
|
const validationErrors = validateConfig(config);
|
|
10158
10824
|
if (validationErrors.length > 0) {
|
|
10159
10825
|
throw ConfigError.invalid(
|
|
10160
10826
|
validationErrors.join("; "),
|
|
10161
|
-
{ path:
|
|
10827
|
+
{ path: path8, errors: validationErrors }
|
|
10162
10828
|
);
|
|
10163
10829
|
}
|
|
10164
|
-
const ext = extname(
|
|
10830
|
+
const ext = extname(path8).toLowerCase();
|
|
10165
10831
|
let content;
|
|
10166
10832
|
if (ext === ".yaml" || ext === ".yml") {
|
|
10167
10833
|
content = dump(config, {
|
|
@@ -10173,30 +10839,30 @@ async function saveConfigFile(path7, config) {
|
|
|
10173
10839
|
} else {
|
|
10174
10840
|
content = JSON.stringify(config, null, 2);
|
|
10175
10841
|
}
|
|
10176
|
-
await writeFile(
|
|
10842
|
+
await writeFile(path8, content, "utf-8");
|
|
10177
10843
|
configCache.clear();
|
|
10178
|
-
logger.info("Config saved successfully", { path: normalizePath(
|
|
10844
|
+
logger.info("Config saved successfully", { path: normalizePath(path8), format: ext });
|
|
10179
10845
|
} catch (error) {
|
|
10180
10846
|
if (error instanceof ConfigError) {
|
|
10181
10847
|
throw error;
|
|
10182
10848
|
}
|
|
10183
10849
|
if (error.code === "EACCES") {
|
|
10184
10850
|
throw new ConfigError(
|
|
10185
|
-
`Permission denied writing config: ${
|
|
10851
|
+
`Permission denied writing config: ${path8}`,
|
|
10186
10852
|
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
10187
10853
|
[
|
|
10188
10854
|
"Check file permissions",
|
|
10189
10855
|
"Run with appropriate user privileges",
|
|
10190
10856
|
"Verify the directory is writable"
|
|
10191
10857
|
],
|
|
10192
|
-
{ path:
|
|
10858
|
+
{ path: path8, error: error.message }
|
|
10193
10859
|
);
|
|
10194
10860
|
}
|
|
10195
10861
|
throw new ConfigError(
|
|
10196
10862
|
`Failed to save config: ${error.message}`,
|
|
10197
10863
|
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
10198
10864
|
["Check file path and permissions"],
|
|
10199
|
-
{ path:
|
|
10865
|
+
{ path: path8, originalError: error.message }
|
|
10200
10866
|
);
|
|
10201
10867
|
}
|
|
10202
10868
|
}
|
|
@@ -10946,10 +11612,10 @@ var configCommand = {
|
|
|
10946
11612
|
} else {
|
|
10947
11613
|
const projectConfig = resolve(process.cwd(), "ax.config.json");
|
|
10948
11614
|
const hiddenConfig = resolve(process.cwd(), ".automatosx", "config.json");
|
|
10949
|
-
const
|
|
10950
|
-
if (
|
|
11615
|
+
const fs8 = await import('fs');
|
|
11616
|
+
if (fs8.existsSync(projectConfig)) {
|
|
10951
11617
|
configPath = projectConfig;
|
|
10952
|
-
} else if (
|
|
11618
|
+
} else if (fs8.existsSync(hiddenConfig)) {
|
|
10953
11619
|
configPath = hiddenConfig;
|
|
10954
11620
|
} else {
|
|
10955
11621
|
configPath = projectConfig;
|
|
@@ -10996,9 +11662,9 @@ var configCommand = {
|
|
|
10996
11662
|
}
|
|
10997
11663
|
}
|
|
10998
11664
|
};
|
|
10999
|
-
async function checkExists(
|
|
11665
|
+
async function checkExists(path8) {
|
|
11000
11666
|
try {
|
|
11001
|
-
await access$1(
|
|
11667
|
+
await access$1(path8, constants.F_OK);
|
|
11002
11668
|
return true;
|
|
11003
11669
|
} catch {
|
|
11004
11670
|
return false;
|
|
@@ -11029,7 +11695,7 @@ async function validateConfigFile(config, verbose) {
|
|
|
11029
11695
|
}
|
|
11030
11696
|
console.log();
|
|
11031
11697
|
}
|
|
11032
|
-
async function resetConfig(
|
|
11698
|
+
async function resetConfig(path8, verbose) {
|
|
11033
11699
|
const { createRequire } = await import('module');
|
|
11034
11700
|
const require2 = createRequire(import.meta.url);
|
|
11035
11701
|
let version = "11.2.6";
|
|
@@ -11044,14 +11710,14 @@ async function resetConfig(path7, verbose) {
|
|
|
11044
11710
|
// Users should rely on IDE JSON Schema plugins that fetch from npm package
|
|
11045
11711
|
version
|
|
11046
11712
|
};
|
|
11047
|
-
await saveConfigFile(
|
|
11713
|
+
await saveConfigFile(path8, config);
|
|
11048
11714
|
printSuccess("Configuration reset to defaults");
|
|
11049
11715
|
if (verbose) {
|
|
11050
11716
|
console.log(chalk5.gray(`
|
|
11051
|
-
Config file: ${
|
|
11717
|
+
Config file: ${path8}
|
|
11052
11718
|
`));
|
|
11053
11719
|
}
|
|
11054
|
-
logger.info("Configuration reset", { path:
|
|
11720
|
+
logger.info("Configuration reset", { path: path8 });
|
|
11055
11721
|
}
|
|
11056
11722
|
async function listConfig(config, verbose) {
|
|
11057
11723
|
console.log(chalk5.bold.cyan("\n\u{1F4CB} AutomatosX Configuration\n"));
|
|
@@ -11130,7 +11796,7 @@ async function getConfig(config, key, verbose) {
|
|
|
11130
11796
|
}
|
|
11131
11797
|
}
|
|
11132
11798
|
}
|
|
11133
|
-
async function setConfig(
|
|
11799
|
+
async function setConfig(path8, config, key, value, verbose) {
|
|
11134
11800
|
let parsedValue = value;
|
|
11135
11801
|
try {
|
|
11136
11802
|
parsedValue = JSON.parse(value);
|
|
@@ -11149,21 +11815,21 @@ async function setConfig(path7, config, key, value, verbose) {
|
|
|
11149
11815
|
console.log();
|
|
11150
11816
|
process.exit(1);
|
|
11151
11817
|
}
|
|
11152
|
-
await saveConfigFile(
|
|
11818
|
+
await saveConfigFile(path8, config);
|
|
11153
11819
|
printSuccess(`Configuration updated: ${key} = ${value}`);
|
|
11154
11820
|
if (verbose) {
|
|
11155
11821
|
console.log(chalk5.gray(`
|
|
11156
|
-
Config file: ${
|
|
11822
|
+
Config file: ${path8}`));
|
|
11157
11823
|
console.log(chalk5.gray("Configuration validated successfully\n"));
|
|
11158
11824
|
}
|
|
11159
11825
|
logger.info("Configuration updated", { key, value });
|
|
11160
11826
|
}
|
|
11161
|
-
function getNestedValue(obj,
|
|
11162
|
-
if (!
|
|
11163
|
-
return
|
|
11827
|
+
function getNestedValue(obj, path8) {
|
|
11828
|
+
if (!path8 || !path8.trim()) return void 0;
|
|
11829
|
+
return path8.split(".").filter(Boolean).reduce((current, key) => current?.[key], obj);
|
|
11164
11830
|
}
|
|
11165
|
-
function setNestedValue(obj,
|
|
11166
|
-
const keys =
|
|
11831
|
+
function setNestedValue(obj, path8, value) {
|
|
11832
|
+
const keys = path8.split(".");
|
|
11167
11833
|
const lastKey = keys.pop();
|
|
11168
11834
|
if (!lastKey) return false;
|
|
11169
11835
|
const target = keys.reduce((current, key) => {
|
|
@@ -13263,7 +13929,7 @@ var setupCommand = {
|
|
|
13263
13929
|
let version = "11.2.6";
|
|
13264
13930
|
try {
|
|
13265
13931
|
const packageJson = JSON.parse(
|
|
13266
|
-
await import('fs/promises').then((
|
|
13932
|
+
await import('fs/promises').then((fs8) => fs8.readFile(join(packageRoot, "package.json"), "utf-8"))
|
|
13267
13933
|
);
|
|
13268
13934
|
version = packageJson.version;
|
|
13269
13935
|
} catch {
|
|
@@ -13375,6 +14041,9 @@ var setupCommand = {
|
|
|
13375
14041
|
console.log(chalk5.cyan("\u{1F504} Installing workflow templates..."));
|
|
13376
14042
|
const workflowCount = await copyWorkflowTemplates(automatosxDir, packageRoot);
|
|
13377
14043
|
console.log(chalk5.green(` \u2713 ${workflowCount} workflow templates installed`));
|
|
14044
|
+
console.log(chalk5.cyan("\u{1F501} Installing iterate mode configuration..."));
|
|
14045
|
+
await copyIterateConfig(automatosxDir, packageRoot);
|
|
14046
|
+
console.log(chalk5.green(" \u2713 Iterate mode patterns and templates installed"));
|
|
13378
14047
|
console.log(chalk5.cyan("\u2699\uFE0F Generating configuration..."));
|
|
13379
14048
|
await createDefaultConfig(configPath, argv.force ?? false, version);
|
|
13380
14049
|
createdResources.push(configPath);
|
|
@@ -13583,9 +14252,9 @@ var setupCommand = {
|
|
|
13583
14252
|
}
|
|
13584
14253
|
}
|
|
13585
14254
|
};
|
|
13586
|
-
async function checkExists2(
|
|
14255
|
+
async function checkExists2(path8) {
|
|
13587
14256
|
try {
|
|
13588
|
-
await access$1(
|
|
14257
|
+
await access$1(path8, constants.F_OK);
|
|
13589
14258
|
return true;
|
|
13590
14259
|
} catch {
|
|
13591
14260
|
return false;
|
|
@@ -13606,8 +14275,12 @@ async function createDirectoryStructure(baseDir) {
|
|
|
13606
14275
|
// v5.1: Session persistence
|
|
13607
14276
|
// v5.2: Removed 'workspaces' - automatosx/PRD and automatosx/tmp created on-demand
|
|
13608
14277
|
join(baseDir, "logs"),
|
|
13609
|
-
join(baseDir, "workflows")
|
|
14278
|
+
join(baseDir, "workflows"),
|
|
13610
14279
|
// v11.0.0: Workflow templates directory
|
|
14280
|
+
join(baseDir, "iterate"),
|
|
14281
|
+
// v11.4.0: Iterate mode patterns and templates
|
|
14282
|
+
join(baseDir, "state")
|
|
14283
|
+
// v11.3.0: Mode state persistence
|
|
13611
14284
|
];
|
|
13612
14285
|
for (const dir of dirs) {
|
|
13613
14286
|
await mkdir(dir, { recursive: true, mode: 493 });
|
|
@@ -13723,6 +14396,167 @@ async function copyWorkflowTemplates(baseDir, packageRoot) {
|
|
|
13723
14396
|
return 4;
|
|
13724
14397
|
}
|
|
13725
14398
|
}
|
|
14399
|
+
async function copyIterateConfig(baseDir, packageRoot) {
|
|
14400
|
+
const iterateDir = join(baseDir, "iterate");
|
|
14401
|
+
const examplesIterateDir = join(packageRoot, "examples/iterate");
|
|
14402
|
+
try {
|
|
14403
|
+
if (await checkExists2(examplesIterateDir)) {
|
|
14404
|
+
const files = await readdir(examplesIterateDir);
|
|
14405
|
+
for (const file of files) {
|
|
14406
|
+
if (file.endsWith(".yaml") || file.endsWith(".yml")) {
|
|
14407
|
+
await copyFile(join(examplesIterateDir, file), join(iterateDir, file));
|
|
14408
|
+
}
|
|
14409
|
+
}
|
|
14410
|
+
return;
|
|
14411
|
+
}
|
|
14412
|
+
} catch {
|
|
14413
|
+
}
|
|
14414
|
+
await createDefaultIterateConfig(iterateDir);
|
|
14415
|
+
}
|
|
14416
|
+
async function createDefaultIterateConfig(iterateDir) {
|
|
14417
|
+
const patternsYaml = `# AutomatosX Iterate Mode - Pattern Library
|
|
14418
|
+
# Production patterns for response classification
|
|
14419
|
+
#
|
|
14420
|
+
# Classification Types:
|
|
14421
|
+
# - confirmation_prompt: AI asking for permission to proceed (auto-respond YES)
|
|
14422
|
+
# - genuine_question: AI asking for user input/decision (PAUSE)
|
|
14423
|
+
# - status_update: AI reporting progress (NO-OP)
|
|
14424
|
+
# - completion_signal: AI indicating task complete (acknowledge)
|
|
14425
|
+
# - blocking_request: AI needs external input (PAUSE)
|
|
14426
|
+
# - error_signal: AI encountered error (PAUSE for recovery)
|
|
14427
|
+
# - rate_limit_or_context: Provider limits hit (RETRY with different provider)
|
|
14428
|
+
|
|
14429
|
+
version: "1.0.0"
|
|
14430
|
+
updatedAt: "${(/* @__PURE__ */ new Date()).toISOString()}"
|
|
14431
|
+
description: "Production pattern library for iterate mode classifier"
|
|
14432
|
+
|
|
14433
|
+
patterns:
|
|
14434
|
+
confirmation_prompt:
|
|
14435
|
+
- pattern: "\\\\b(should I|shall I|do you want me to|would you like me to)\\\\b.*(proceed|continue|start|begin|go ahead)"
|
|
14436
|
+
priority: 10
|
|
14437
|
+
confidence: 0.95
|
|
14438
|
+
- pattern: "\\\\b(ready to|prepared to)\\\\b.*(proceed|continue|start|begin|implement)"
|
|
14439
|
+
priority: 9
|
|
14440
|
+
confidence: 0.9
|
|
14441
|
+
- pattern: "\\\\b(continue|proceed|go ahead)\\\\s*\\\\?"
|
|
14442
|
+
priority: 8
|
|
14443
|
+
confidence: 0.85
|
|
14444
|
+
- pattern: "\\\\b(is that|does that|sound)\\\\s+(okay|ok|good|correct|right)\\\\s*\\\\?"
|
|
14445
|
+
priority: 7
|
|
14446
|
+
confidence: 0.8
|
|
14447
|
+
|
|
14448
|
+
genuine_question:
|
|
14449
|
+
- pattern: "\\\\b(which|what)\\\\b.*(approach|method|option|implementation|strategy|framework|library)\\\\b.*(should|would|prefer|recommend)"
|
|
14450
|
+
priority: 10
|
|
14451
|
+
confidence: 0.95
|
|
14452
|
+
- pattern: "\\\\b(option [A-Z]|choice \\\\d|alternative \\\\d)\\\\b"
|
|
14453
|
+
priority: 10
|
|
14454
|
+
confidence: 0.95
|
|
14455
|
+
- pattern: "\\\\bwould you prefer\\\\b"
|
|
14456
|
+
priority: 10
|
|
14457
|
+
confidence: 0.95
|
|
14458
|
+
- pattern: "\\\\bcould you (clarify|explain|specify|provide)\\\\b"
|
|
14459
|
+
priority: 8
|
|
14460
|
+
confidence: 0.85
|
|
14461
|
+
|
|
14462
|
+
status_update:
|
|
14463
|
+
- pattern: "\\\\b(working on|implementing|creating|building|setting up|configuring)\\\\b"
|
|
14464
|
+
priority: 10
|
|
14465
|
+
confidence: 0.9
|
|
14466
|
+
- pattern: "\\\\b(analyzing|reviewing|examining|checking|looking at)\\\\b"
|
|
14467
|
+
priority: 9
|
|
14468
|
+
confidence: 0.85
|
|
14469
|
+
- pattern: "\\\\bI('ll| will| am going to)\\\\b.*(now|first|next|then)\\\\b"
|
|
14470
|
+
priority: 8
|
|
14471
|
+
confidence: 0.8
|
|
14472
|
+
|
|
14473
|
+
completion_signal:
|
|
14474
|
+
- pattern: "\\\\b(all (tasks|items|changes|updates)|everything)\\\\s+(completed|done|finished|implemented)"
|
|
14475
|
+
priority: 10
|
|
14476
|
+
confidence: 0.95
|
|
14477
|
+
- pattern: "\\\\bsuccessfully (completed|implemented|created|fixed|resolved)"
|
|
14478
|
+
priority: 9
|
|
14479
|
+
confidence: 0.9
|
|
14480
|
+
- pattern: "\\\\blet me know if (you need|there('s| is)) anything else"
|
|
14481
|
+
priority: 7
|
|
14482
|
+
confidence: 0.8
|
|
14483
|
+
|
|
14484
|
+
blocking_request:
|
|
14485
|
+
- pattern: "\\\\b(API key|credentials|password|token|secret)\\\\s+(needed|required|missing)"
|
|
14486
|
+
priority: 10
|
|
14487
|
+
confidence: 0.95
|
|
14488
|
+
- pattern: "\\\\bplease (provide|enter|specify|supply)\\\\b.*(key|password|token|credential)"
|
|
14489
|
+
priority: 10
|
|
14490
|
+
confidence: 0.95
|
|
14491
|
+
- pattern: "\\\\b(need|require|waiting for)\\\\s+(your|user)\\\\s+(input|approval|confirmation|decision)"
|
|
14492
|
+
priority: 10
|
|
14493
|
+
confidence: 0.95
|
|
14494
|
+
|
|
14495
|
+
error_signal:
|
|
14496
|
+
- pattern: "\\\\b(error|failed|failure|exception)\\\\s*:"
|
|
14497
|
+
priority: 10
|
|
14498
|
+
confidence: 0.95
|
|
14499
|
+
- pattern: "\\\\b(cannot|can't|couldn't|unable to)\\\\s+(find|locate|access|connect|read|write)"
|
|
14500
|
+
priority: 9
|
|
14501
|
+
confidence: 0.9
|
|
14502
|
+
- pattern: "\\\\b(test|build|compilation)\\\\s+(failed|error)"
|
|
14503
|
+
priority: 9
|
|
14504
|
+
confidence: 0.9
|
|
14505
|
+
|
|
14506
|
+
rate_limit_or_context:
|
|
14507
|
+
- pattern: "\\\\brate limit\\\\s*(exceeded|reached|hit)"
|
|
14508
|
+
priority: 10
|
|
14509
|
+
confidence: 0.95
|
|
14510
|
+
- pattern: "\\\\bcontext (window|limit)\\\\s*(exceeded|full|reached)"
|
|
14511
|
+
priority: 10
|
|
14512
|
+
confidence: 0.95
|
|
14513
|
+
- pattern: "\\\\btoo many requests"
|
|
14514
|
+
priority: 10
|
|
14515
|
+
confidence: 0.95
|
|
14516
|
+
`;
|
|
14517
|
+
const templatesYaml = `# AutomatosX Iterate Mode - Template Library
|
|
14518
|
+
# Production templates for auto-response generation
|
|
14519
|
+
|
|
14520
|
+
version: "1.0.0"
|
|
14521
|
+
updatedAt: "${(/* @__PURE__ */ new Date()).toISOString()}"
|
|
14522
|
+
description: "Production template library for iterate mode auto-responder"
|
|
14523
|
+
|
|
14524
|
+
templates:
|
|
14525
|
+
confirmation_prompt:
|
|
14526
|
+
- template: "Yes, please proceed."
|
|
14527
|
+
priority: 10
|
|
14528
|
+
provider: null
|
|
14529
|
+
- template: "Yes, continue with that approach."
|
|
14530
|
+
priority: 9
|
|
14531
|
+
provider: null
|
|
14532
|
+
- template: "Sounds good, go ahead."
|
|
14533
|
+
priority: 8
|
|
14534
|
+
provider: null
|
|
14535
|
+
- template: "Proceed."
|
|
14536
|
+
priority: 7
|
|
14537
|
+
provider: null
|
|
14538
|
+
- template: "Continue."
|
|
14539
|
+
priority: 7
|
|
14540
|
+
provider: null
|
|
14541
|
+
|
|
14542
|
+
completion_signal:
|
|
14543
|
+
- template: "Thank you."
|
|
14544
|
+
priority: 10
|
|
14545
|
+
provider: null
|
|
14546
|
+
- template: "Great work."
|
|
14547
|
+
priority: 9
|
|
14548
|
+
provider: null
|
|
14549
|
+
|
|
14550
|
+
status_update: []
|
|
14551
|
+
genuine_question: []
|
|
14552
|
+
blocking_request: []
|
|
14553
|
+
error_signal: []
|
|
14554
|
+
rate_limit_or_context: []
|
|
14555
|
+
`;
|
|
14556
|
+
await writeFile(join(iterateDir, "patterns.yaml"), patternsYaml, "utf-8");
|
|
14557
|
+
await writeFile(join(iterateDir, "templates.yaml"), templatesYaml, "utf-8");
|
|
14558
|
+
logger.info("Created default iterate mode configuration", { iterateDir });
|
|
14559
|
+
}
|
|
13726
14560
|
async function createDefaultWorkflowTemplates(targetDir) {
|
|
13727
14561
|
const authFlow = `# Authentication Implementation Workflow
|
|
13728
14562
|
# Usage: ax run backend "implement user login" --workflow auth-flow
|
|
@@ -14932,11 +15766,11 @@ async function detectExpressRoutes(projectDir) {
|
|
|
14932
15766
|
while ((match = routeRegex.exec(content)) !== null) {
|
|
14933
15767
|
if (match[1] && match[2]) {
|
|
14934
15768
|
const method = match[1].toUpperCase();
|
|
14935
|
-
const
|
|
15769
|
+
const path8 = match[2];
|
|
14936
15770
|
endpoints.push({
|
|
14937
15771
|
method,
|
|
14938
|
-
path:
|
|
14939
|
-
group:
|
|
15772
|
+
path: path8,
|
|
15773
|
+
group: path8.split("/")[1] || "api"
|
|
14940
15774
|
});
|
|
14941
15775
|
}
|
|
14942
15776
|
}
|
|
@@ -16041,12 +16875,12 @@ var listCommand = {
|
|
|
16041
16875
|
};
|
|
16042
16876
|
async function listAgents(pathResolver, format) {
|
|
16043
16877
|
const agentsDir = pathResolver.getAgentsDirectory();
|
|
16044
|
-
const { existsSync:
|
|
16878
|
+
const { existsSync: existsSync27 } = await import('fs');
|
|
16045
16879
|
const projectDir = await detectProjectRoot();
|
|
16046
16880
|
const examplesDir = join(projectDir, "examples", "agents");
|
|
16047
16881
|
try {
|
|
16048
16882
|
const agentFiles = [];
|
|
16049
|
-
if (
|
|
16883
|
+
if (existsSync27(agentsDir)) {
|
|
16050
16884
|
const files = await readdir(agentsDir);
|
|
16051
16885
|
for (const file of files) {
|
|
16052
16886
|
if (file.endsWith(".yaml") || file.endsWith(".yml")) {
|
|
@@ -16058,7 +16892,7 @@ async function listAgents(pathResolver, format) {
|
|
|
16058
16892
|
}
|
|
16059
16893
|
}
|
|
16060
16894
|
}
|
|
16061
|
-
if (
|
|
16895
|
+
if (existsSync27(examplesDir)) {
|
|
16062
16896
|
const files = await readdir(examplesDir);
|
|
16063
16897
|
for (const file of files) {
|
|
16064
16898
|
if (file.endsWith(".yaml") || file.endsWith(".yml")) {
|
|
@@ -17930,18 +18764,18 @@ var ProviderSessionManager = class {
|
|
|
17930
18764
|
};
|
|
17931
18765
|
var providerSessionInstances = /* @__PURE__ */ new Map();
|
|
17932
18766
|
async function getProviderSession(workspacePath) {
|
|
17933
|
-
let
|
|
18767
|
+
let path8;
|
|
17934
18768
|
{
|
|
17935
18769
|
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
17936
|
-
|
|
18770
|
+
path8 = process.cwd();
|
|
17937
18771
|
} else {
|
|
17938
|
-
|
|
18772
|
+
path8 = await detectProjectRoot();
|
|
17939
18773
|
}
|
|
17940
18774
|
}
|
|
17941
|
-
if (!providerSessionInstances.has(
|
|
17942
|
-
providerSessionInstances.set(
|
|
18775
|
+
if (!providerSessionInstances.has(path8)) {
|
|
18776
|
+
providerSessionInstances.set(path8, new ProviderSessionManager(path8));
|
|
17943
18777
|
}
|
|
17944
|
-
return providerSessionInstances.get(
|
|
18778
|
+
return providerSessionInstances.get(path8);
|
|
17945
18779
|
}
|
|
17946
18780
|
|
|
17947
18781
|
// src/core/router/router.ts
|
|
@@ -19711,6 +20545,9 @@ var MemoryManager = class _MemoryManager {
|
|
|
19711
20545
|
if (!Number.isInteger(offsetValue) || offsetValue < 0) {
|
|
19712
20546
|
throw new Error(`Invalid offset value: ${options.offset}. Must be a non-negative integer.`);
|
|
19713
20547
|
}
|
|
20548
|
+
if (!limitClause) {
|
|
20549
|
+
limitClause = "LIMIT -1";
|
|
20550
|
+
}
|
|
19714
20551
|
offsetClause = `OFFSET ${offsetValue}`;
|
|
19715
20552
|
}
|
|
19716
20553
|
const sql = `
|
|
@@ -20056,14 +20893,21 @@ var MemoryManager = class _MemoryManager {
|
|
|
20056
20893
|
this.initialized = false;
|
|
20057
20894
|
this.entryCount = 0;
|
|
20058
20895
|
this.statements = {};
|
|
20059
|
-
const { rename:
|
|
20060
|
-
await
|
|
20896
|
+
const { rename: rename6 } = await import('fs/promises');
|
|
20897
|
+
await rename6(tempPath, this.config.dbPath);
|
|
20061
20898
|
this.db = new Database2(this.config.dbPath);
|
|
20062
20899
|
this.db.pragma("journal_mode = WAL");
|
|
20063
20900
|
this.db.pragma(`busy_timeout = ${this.config.busyTimeout}`);
|
|
20064
20901
|
await this.initialize();
|
|
20065
20902
|
logger.info("Database restored successfully (atomic operation)", { srcPath: normalizePath(srcPath) });
|
|
20066
20903
|
} catch (error) {
|
|
20904
|
+
try {
|
|
20905
|
+
DatabaseFactory.close(this.db);
|
|
20906
|
+
} finally {
|
|
20907
|
+
this.initialized = false;
|
|
20908
|
+
this.entryCount = 0;
|
|
20909
|
+
this.statements = {};
|
|
20910
|
+
}
|
|
20067
20911
|
throw new MemoryError(
|
|
20068
20912
|
`Failed to restore database: ${error.message}`,
|
|
20069
20913
|
"DATABASE_ERROR",
|
|
@@ -20614,6 +21458,10 @@ var SessionManager = class _SessionManager {
|
|
|
20614
21458
|
MAX_TASKS_PER_SESSION = 1e3;
|
|
20615
21459
|
/** UUID v4 validation regex (static for performance) */
|
|
20616
21460
|
static UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
21461
|
+
/** Tracks whether manager has been destroyed to prevent late saves */
|
|
21462
|
+
destroyed = false;
|
|
21463
|
+
/** Last persistence error (reported during flush) */
|
|
21464
|
+
lastSaveError;
|
|
20617
21465
|
/**
|
|
20618
21466
|
* Validate session ID format (must be valid UUID v4)
|
|
20619
21467
|
*
|
|
@@ -21208,6 +22056,7 @@ var SessionManager = class _SessionManager {
|
|
|
21208
22056
|
* ```
|
|
21209
22057
|
*/
|
|
21210
22058
|
async destroy() {
|
|
22059
|
+
this.destroyed = true;
|
|
21211
22060
|
if (this.saveTimeout) {
|
|
21212
22061
|
clearTimeout(this.saveTimeout);
|
|
21213
22062
|
this.saveTimeout = void 0;
|
|
@@ -21218,9 +22067,12 @@ var SessionManager = class _SessionManager {
|
|
|
21218
22067
|
logger.error("Error flushing save during destroy", {
|
|
21219
22068
|
error: error.message
|
|
21220
22069
|
});
|
|
22070
|
+
this.lastSaveError = void 0;
|
|
21221
22071
|
}
|
|
22072
|
+
const sessionCount = this.activeSessions.size;
|
|
22073
|
+
this.activeSessions.clear();
|
|
21222
22074
|
logger.debug("SessionManager destroyed", {
|
|
21223
|
-
sessions:
|
|
22075
|
+
sessions: sessionCount
|
|
21224
22076
|
});
|
|
21225
22077
|
}
|
|
21226
22078
|
/**
|
|
@@ -21245,11 +22097,21 @@ var SessionManager = class _SessionManager {
|
|
|
21245
22097
|
this.pendingSave = void 0;
|
|
21246
22098
|
});
|
|
21247
22099
|
await this.pendingSave;
|
|
22100
|
+
if (this.lastSaveError) {
|
|
22101
|
+
const err = this.lastSaveError;
|
|
22102
|
+
this.lastSaveError = void 0;
|
|
22103
|
+
throw err;
|
|
22104
|
+
}
|
|
21248
22105
|
return;
|
|
21249
22106
|
}
|
|
21250
22107
|
if (this.pendingSave) {
|
|
21251
22108
|
try {
|
|
21252
22109
|
await this.pendingSave;
|
|
22110
|
+
if (this.lastSaveError) {
|
|
22111
|
+
const err = this.lastSaveError;
|
|
22112
|
+
this.lastSaveError = void 0;
|
|
22113
|
+
throw err;
|
|
22114
|
+
}
|
|
21253
22115
|
} catch (err) {
|
|
21254
22116
|
throw err;
|
|
21255
22117
|
}
|
|
@@ -21414,7 +22276,7 @@ var SessionManager = class _SessionManager {
|
|
|
21414
22276
|
* @private
|
|
21415
22277
|
*/
|
|
21416
22278
|
saveToFile() {
|
|
21417
|
-
if (!this.persistencePath) {
|
|
22279
|
+
if (!this.persistencePath || this.destroyed) {
|
|
21418
22280
|
return;
|
|
21419
22281
|
}
|
|
21420
22282
|
if (this.saveTimeout) {
|
|
@@ -21427,18 +22289,18 @@ var SessionManager = class _SessionManager {
|
|
|
21427
22289
|
}
|
|
21428
22290
|
this.saveNeeded = false;
|
|
21429
22291
|
const executeNextSave = async () => {
|
|
21430
|
-
|
|
22292
|
+
try {
|
|
22293
|
+
await this.doSave();
|
|
22294
|
+
this.lastSaveError = void 0;
|
|
22295
|
+
} catch (err) {
|
|
22296
|
+
this.lastSaveError = err;
|
|
22297
|
+
}
|
|
21431
22298
|
if (this.saveNeeded) {
|
|
21432
22299
|
this.saveNeeded = false;
|
|
21433
22300
|
return executeNextSave();
|
|
21434
22301
|
}
|
|
21435
22302
|
};
|
|
21436
|
-
this.pendingSave = executeNextSave().
|
|
21437
|
-
logger.error("Debounced save failed", {
|
|
21438
|
-
error: err.message
|
|
21439
|
-
});
|
|
21440
|
-
throw err;
|
|
21441
|
-
}).finally(() => {
|
|
22303
|
+
this.pendingSave = executeNextSave().finally(() => {
|
|
21442
22304
|
this.pendingSave = void 0;
|
|
21443
22305
|
});
|
|
21444
22306
|
}, 100);
|
|
@@ -23759,9 +24621,9 @@ var DependencyGraphBuilder = class {
|
|
|
23759
24621
|
detectCycles(graph) {
|
|
23760
24622
|
const visiting = /* @__PURE__ */ new Set();
|
|
23761
24623
|
const visited = /* @__PURE__ */ new Set();
|
|
23762
|
-
const visit = (nodeName,
|
|
24624
|
+
const visit = (nodeName, path8) => {
|
|
23763
24625
|
if (visiting.has(nodeName)) {
|
|
23764
|
-
throw new Error(`Circular dependency detected: ${[...
|
|
24626
|
+
throw new Error(`Circular dependency detected: ${[...path8, nodeName].join(" \u2192 ")}`);
|
|
23765
24627
|
}
|
|
23766
24628
|
if (visited.has(nodeName)) {
|
|
23767
24629
|
return;
|
|
@@ -23776,7 +24638,7 @@ var DependencyGraphBuilder = class {
|
|
|
23776
24638
|
}
|
|
23777
24639
|
visiting.add(nodeName);
|
|
23778
24640
|
for (const dependency of node.dependencies) {
|
|
23779
|
-
visit(dependency, [...
|
|
24641
|
+
visit(dependency, [...path8, nodeName]);
|
|
23780
24642
|
}
|
|
23781
24643
|
visiting.delete(nodeName);
|
|
23782
24644
|
visited.add(nodeName);
|
|
@@ -25509,59 +26371,59 @@ var DANGEROUS_PATH_PATTERNS = [
|
|
|
25509
26371
|
// Common data drive (Windows, alt format)
|
|
25510
26372
|
];
|
|
25511
26373
|
var SUSPICIOUS_PATH_CHARS = /[<>:|"]/;
|
|
25512
|
-
function validatePathParameter(
|
|
25513
|
-
if (!
|
|
26374
|
+
function validatePathParameter(path8, paramName, projectRoot = process.cwd()) {
|
|
26375
|
+
if (!path8 || path8.trim() === "") {
|
|
25514
26376
|
throw new ValidationError(
|
|
25515
26377
|
`Invalid ${paramName}: path cannot be empty`,
|
|
25516
26378
|
-32602 /* InvalidParams */,
|
|
25517
|
-
{ path:
|
|
26379
|
+
{ path: path8, paramName }
|
|
25518
26380
|
);
|
|
25519
26381
|
}
|
|
25520
26382
|
for (const pattern of DANGEROUS_PATH_PATTERNS) {
|
|
25521
|
-
if (
|
|
26383
|
+
if (path8.includes(pattern)) {
|
|
25522
26384
|
throw new ValidationError(
|
|
25523
26385
|
`Invalid ${paramName}: path contains dangerous pattern "${pattern}"`,
|
|
25524
26386
|
-32602 /* InvalidParams */,
|
|
25525
|
-
{ path:
|
|
26387
|
+
{ path: path8, paramName, pattern }
|
|
25526
26388
|
);
|
|
25527
26389
|
}
|
|
25528
26390
|
}
|
|
25529
|
-
if (isAbsolute(
|
|
26391
|
+
if (isAbsolute(path8)) {
|
|
25530
26392
|
throw new ValidationError(
|
|
25531
26393
|
`Invalid ${paramName}: absolute paths are not allowed`,
|
|
25532
26394
|
-32602 /* InvalidParams */,
|
|
25533
|
-
{ path:
|
|
26395
|
+
{ path: path8, paramName }
|
|
25534
26396
|
);
|
|
25535
26397
|
}
|
|
25536
26398
|
try {
|
|
25537
|
-
const resolvedPath = resolve(projectRoot,
|
|
26399
|
+
const resolvedPath = resolve(projectRoot, path8);
|
|
25538
26400
|
const normalizedRoot = resolve(projectRoot);
|
|
25539
26401
|
if (!resolvedPath.startsWith(normalizedRoot + sep) && resolvedPath !== normalizedRoot) {
|
|
25540
26402
|
throw new ValidationError(
|
|
25541
26403
|
`Invalid ${paramName}: path escapes project boundary`,
|
|
25542
26404
|
-32602 /* InvalidParams */,
|
|
25543
|
-
{ path:
|
|
26405
|
+
{ path: path8, paramName, projectRoot, resolvedPath }
|
|
25544
26406
|
);
|
|
25545
26407
|
}
|
|
25546
26408
|
} catch (error) {
|
|
25547
26409
|
throw new ValidationError(
|
|
25548
26410
|
`Invalid ${paramName}: path resolution failed`,
|
|
25549
26411
|
-32602 /* InvalidParams */,
|
|
25550
|
-
{ path:
|
|
26412
|
+
{ path: path8, paramName, error: String(error) }
|
|
25551
26413
|
);
|
|
25552
26414
|
}
|
|
25553
|
-
if (
|
|
26415
|
+
if (path8.includes("\0")) {
|
|
25554
26416
|
throw new ValidationError(
|
|
25555
26417
|
`Invalid ${paramName}: path contains null byte`,
|
|
25556
26418
|
-32602 /* InvalidParams */,
|
|
25557
|
-
{ path:
|
|
26419
|
+
{ path: path8, paramName }
|
|
25558
26420
|
);
|
|
25559
26421
|
}
|
|
25560
|
-
if (SUSPICIOUS_PATH_CHARS.test(
|
|
26422
|
+
if (SUSPICIOUS_PATH_CHARS.test(path8)) {
|
|
25561
26423
|
throw new ValidationError(
|
|
25562
26424
|
`Invalid ${paramName}: path contains invalid characters`,
|
|
25563
26425
|
-32602 /* InvalidParams */,
|
|
25564
|
-
{ path:
|
|
26426
|
+
{ path: path8, paramName }
|
|
25565
26427
|
);
|
|
25566
26428
|
}
|
|
25567
26429
|
}
|
|
@@ -26257,8 +27119,8 @@ function createMemoryExportHandler(deps) {
|
|
|
26257
27119
|
return async (input) => {
|
|
26258
27120
|
logger.info("[MCP] memory_export called", { input });
|
|
26259
27121
|
try {
|
|
26260
|
-
const { path:
|
|
26261
|
-
const absolutePath = resolveExportPath(deps.pathResolver,
|
|
27122
|
+
const { path: path8 } = input;
|
|
27123
|
+
const absolutePath = resolveExportPath(deps.pathResolver, path8);
|
|
26262
27124
|
const exported = await deps.memoryManager.exportToJSON(absolutePath);
|
|
26263
27125
|
const result = {
|
|
26264
27126
|
success: true,
|
|
@@ -26294,8 +27156,8 @@ function createMemoryImportHandler(deps) {
|
|
|
26294
27156
|
return async (input) => {
|
|
26295
27157
|
logger.info("[MCP] memory_import called", { input });
|
|
26296
27158
|
try {
|
|
26297
|
-
const { path:
|
|
26298
|
-
const absolutePath = resolveImportPath(deps.pathResolver,
|
|
27159
|
+
const { path: path8 } = input;
|
|
27160
|
+
const absolutePath = resolveImportPath(deps.pathResolver, path8);
|
|
26299
27161
|
const imported = await deps.memoryManager.importFromJSON(absolutePath);
|
|
26300
27162
|
const result = {
|
|
26301
27163
|
success: true,
|
|
@@ -26848,7 +27710,8 @@ var McpClient = class extends EventEmitter {
|
|
|
26848
27710
|
* Send JSON-RPC request and wait for response
|
|
26849
27711
|
*/
|
|
26850
27712
|
async sendRequest(method, params, timeout) {
|
|
26851
|
-
|
|
27713
|
+
const stdin = this.process?.stdin;
|
|
27714
|
+
if (!stdin) {
|
|
26852
27715
|
throw new Error("MCP client not connected");
|
|
26853
27716
|
}
|
|
26854
27717
|
const id = this.nextRequestId++;
|
|
@@ -26870,7 +27733,18 @@ var McpClient = class extends EventEmitter {
|
|
|
26870
27733
|
timeout: timeoutHandle
|
|
26871
27734
|
});
|
|
26872
27735
|
const message = JSON.stringify(request) + "\n";
|
|
26873
|
-
|
|
27736
|
+
try {
|
|
27737
|
+
stdin.write(message);
|
|
27738
|
+
} catch (error) {
|
|
27739
|
+
const pending = this.pendingRequests.get(id);
|
|
27740
|
+
if (pending) {
|
|
27741
|
+
clearTimeout(pending.timeout);
|
|
27742
|
+
this.pendingRequests.delete(id);
|
|
27743
|
+
}
|
|
27744
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
27745
|
+
reject(err);
|
|
27746
|
+
return;
|
|
27747
|
+
}
|
|
26874
27748
|
logger.debug("[MCP Client] Sent request", {
|
|
26875
27749
|
id,
|
|
26876
27750
|
method,
|
|
@@ -26880,6 +27754,10 @@ var McpClient = class extends EventEmitter {
|
|
|
26880
27754
|
}
|
|
26881
27755
|
/**
|
|
26882
27756
|
* Send JSON-RPC notification (no response expected)
|
|
27757
|
+
*
|
|
27758
|
+
* BUG FIX: Handle write errors gracefully. If the process has exited or the
|
|
27759
|
+
* pipe is broken, write() could throw an uncaught exception that would crash
|
|
27760
|
+
* the application.
|
|
26883
27761
|
*/
|
|
26884
27762
|
sendNotification(method, params) {
|
|
26885
27763
|
if (!this.process?.stdin) {
|
|
@@ -26891,7 +27769,14 @@ var McpClient = class extends EventEmitter {
|
|
|
26891
27769
|
params
|
|
26892
27770
|
};
|
|
26893
27771
|
const message = JSON.stringify(notification) + "\n";
|
|
26894
|
-
|
|
27772
|
+
try {
|
|
27773
|
+
this.process.stdin.write(message);
|
|
27774
|
+
} catch (error) {
|
|
27775
|
+
logger.debug("[MCP Client] Failed to send notification (pipe may be closed)", {
|
|
27776
|
+
method,
|
|
27777
|
+
error: error instanceof Error ? error.message : String(error)
|
|
27778
|
+
});
|
|
27779
|
+
}
|
|
26895
27780
|
}
|
|
26896
27781
|
/**
|
|
26897
27782
|
* Handle incoming JSON-RPC message
|
|
@@ -27242,6 +28127,11 @@ var McpClientPool = class extends EventEmitter {
|
|
|
27242
28127
|
}
|
|
27243
28128
|
/**
|
|
27244
28129
|
* Check if pool has available capacity for provider
|
|
28130
|
+
*
|
|
28131
|
+
* BUG FIX: Account for pendingConnections to avoid reporting capacity
|
|
28132
|
+
* when concurrent connection creations are in progress. Previously,
|
|
28133
|
+
* hasCapacity() could return true even when all slots were about to be
|
|
28134
|
+
* filled by in-flight createConnection() calls, causing over-allocation.
|
|
27245
28135
|
*/
|
|
27246
28136
|
hasCapacity(provider) {
|
|
27247
28137
|
const pool = this.pools.get(provider);
|
|
@@ -27255,7 +28145,8 @@ var McpClientPool = class extends EventEmitter {
|
|
|
27255
28145
|
connectedCount++;
|
|
27256
28146
|
}
|
|
27257
28147
|
}
|
|
27258
|
-
|
|
28148
|
+
const totalAllocated = connectedCount + pool.pendingConnections;
|
|
28149
|
+
return totalAllocated < this.config.maxConnectionsPerProvider;
|
|
27259
28150
|
}
|
|
27260
28151
|
// ============================================
|
|
27261
28152
|
// Private Methods
|
|
@@ -27378,6 +28269,10 @@ var McpClientPool = class extends EventEmitter {
|
|
|
27378
28269
|
}
|
|
27379
28270
|
}
|
|
27380
28271
|
for (const pooledClient of toRemove) {
|
|
28272
|
+
if (pooledClient.inUse) {
|
|
28273
|
+
logger.debug("[MCP Pool] Skipping idle client removal - now in use", { provider });
|
|
28274
|
+
continue;
|
|
28275
|
+
}
|
|
27381
28276
|
logger.debug("[MCP Pool] Closing idle connection", {
|
|
27382
28277
|
provider,
|
|
27383
28278
|
idleTimeMs: now - pooledClient.lastUsed
|
|
@@ -29606,20 +30501,20 @@ var LifecycleLogger = class {
|
|
|
29606
30501
|
}
|
|
29607
30502
|
/**
|
|
29608
30503
|
* Rotate log file if it exceeds size limit
|
|
30504
|
+
*
|
|
30505
|
+
* BUG FIX: Previously used dynamic imports for fs/fs.promises which are already
|
|
30506
|
+
* imported at module level, causing unnecessary overhead. Also had a TOCTOU race
|
|
30507
|
+
* between existsSync and statSync - now uses async stat() which handles ENOENT
|
|
30508
|
+
* gracefully without the race condition.
|
|
29609
30509
|
*/
|
|
29610
30510
|
async rotateLogIfNeeded(logPath) {
|
|
29611
|
-
if (!existsSync(logPath)) {
|
|
29612
|
-
return;
|
|
29613
|
-
}
|
|
29614
30511
|
try {
|
|
29615
|
-
const
|
|
29616
|
-
const stats = statSync5(logPath);
|
|
30512
|
+
const stats = await stat(logPath);
|
|
29617
30513
|
const sizeMB = stats.size / (1024 * 1024);
|
|
29618
30514
|
if (sizeMB > this.maxLogSizeMB) {
|
|
29619
30515
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
29620
30516
|
const archivePath = logPath.replace(".jsonl", `-${timestamp}.jsonl`);
|
|
29621
|
-
|
|
29622
|
-
await rename4(logPath, archivePath);
|
|
30517
|
+
await rename(logPath, archivePath);
|
|
29623
30518
|
logger.info("LifecycleLogger: Rotated log file", {
|
|
29624
30519
|
oldPath: logPath,
|
|
29625
30520
|
newPath: archivePath,
|
|
@@ -29627,6 +30522,9 @@ var LifecycleLogger = class {
|
|
|
29627
30522
|
});
|
|
29628
30523
|
}
|
|
29629
30524
|
} catch (error) {
|
|
30525
|
+
if (error.code === "ENOENT") {
|
|
30526
|
+
return;
|
|
30527
|
+
}
|
|
29630
30528
|
logger.error("LifecycleLogger: Failed to rotate log file", { error });
|
|
29631
30529
|
}
|
|
29632
30530
|
}
|
|
@@ -29655,6 +30553,8 @@ var MetricsCollector = class {
|
|
|
29655
30553
|
failedRequestCounts = /* @__PURE__ */ new Map();
|
|
29656
30554
|
responseTimes = /* @__PURE__ */ new Map();
|
|
29657
30555
|
restartCounts = /* @__PURE__ */ new Map();
|
|
30556
|
+
/** Track which servers have had their first start recorded */
|
|
30557
|
+
serverStarted = /* @__PURE__ */ new Set();
|
|
29658
30558
|
constructor(options = {}) {
|
|
29659
30559
|
this.enabled = options.enabled ?? true;
|
|
29660
30560
|
this.collectionIntervalMs = options.collectionIntervalMs ?? 1e4;
|
|
@@ -29693,15 +30593,27 @@ var MetricsCollector = class {
|
|
|
29693
30593
|
}
|
|
29694
30594
|
/**
|
|
29695
30595
|
* Record server start
|
|
30596
|
+
*
|
|
30597
|
+
* BUG FIX: Previously incremented restartCount on every start including the first one,
|
|
30598
|
+
* so a server that never restarted would show restartCount: 1. Now correctly tracks
|
|
30599
|
+
* only actual restarts (starts after the first one).
|
|
29696
30600
|
*/
|
|
29697
30601
|
recordServerStart(serverName) {
|
|
29698
30602
|
if (!this.enabled) return;
|
|
29699
|
-
|
|
29700
|
-
|
|
29701
|
-
|
|
29702
|
-
|
|
29703
|
-
|
|
29704
|
-
|
|
30603
|
+
if (this.serverStarted.has(serverName)) {
|
|
30604
|
+
const currentRestarts = this.restartCounts.get(serverName) || 0;
|
|
30605
|
+
this.restartCounts.set(serverName, currentRestarts + 1);
|
|
30606
|
+
logger.debug("MetricsCollector: Recorded server restart", {
|
|
30607
|
+
serverName,
|
|
30608
|
+
restartCount: currentRestarts + 1
|
|
30609
|
+
});
|
|
30610
|
+
} else {
|
|
30611
|
+
this.serverStarted.add(serverName);
|
|
30612
|
+
this.restartCounts.set(serverName, 0);
|
|
30613
|
+
logger.debug("MetricsCollector: Recorded server first start", {
|
|
30614
|
+
serverName
|
|
30615
|
+
});
|
|
30616
|
+
}
|
|
29705
30617
|
}
|
|
29706
30618
|
/**
|
|
29707
30619
|
* Record request
|
|
@@ -29834,6 +30746,7 @@ var MetricsCollector = class {
|
|
|
29834
30746
|
this.failedRequestCounts.delete(serverName);
|
|
29835
30747
|
this.responseTimes.delete(serverName);
|
|
29836
30748
|
this.restartCounts.delete(serverName);
|
|
30749
|
+
this.serverStarted.delete(serverName);
|
|
29837
30750
|
logger.info("MetricsCollector: Cleared history for server", { serverName });
|
|
29838
30751
|
} else {
|
|
29839
30752
|
this.metricsHistory.clear();
|
|
@@ -29841,6 +30754,7 @@ var MetricsCollector = class {
|
|
|
29841
30754
|
this.failedRequestCounts.clear();
|
|
29842
30755
|
this.responseTimes.clear();
|
|
29843
30756
|
this.restartCounts.clear();
|
|
30757
|
+
this.serverStarted.clear();
|
|
29844
30758
|
logger.info("MetricsCollector: Cleared all history");
|
|
29845
30759
|
}
|
|
29846
30760
|
}
|
|
@@ -29926,6 +30840,8 @@ var ResourceEnforcer = class {
|
|
|
29926
30840
|
violationHandlers = [];
|
|
29927
30841
|
violationTimestamps = /* @__PURE__ */ new Map();
|
|
29928
30842
|
monitoredServers = /* @__PURE__ */ new Map();
|
|
30843
|
+
/** Track throttle resume timers to prevent leaks on stop() */
|
|
30844
|
+
throttleTimers = /* @__PURE__ */ new Map();
|
|
29929
30845
|
constructor(options) {
|
|
29930
30846
|
this.defaultLimits = options.defaultLimits;
|
|
29931
30847
|
this.serverLimits = options.serverLimits || /* @__PURE__ */ new Map();
|
|
@@ -29952,13 +30868,20 @@ var ResourceEnforcer = class {
|
|
|
29952
30868
|
}
|
|
29953
30869
|
/**
|
|
29954
30870
|
* Stop enforcing resource limits
|
|
30871
|
+
*
|
|
30872
|
+
* BUG FIX: Now also clears all pending throttle resume timers to prevent
|
|
30873
|
+
* them from executing after stop() is called.
|
|
29955
30874
|
*/
|
|
29956
30875
|
stop() {
|
|
29957
30876
|
if (this.checkInterval) {
|
|
29958
30877
|
clearInterval(this.checkInterval);
|
|
29959
30878
|
this.checkInterval = void 0;
|
|
29960
|
-
logger.info("ResourceEnforcer: Stopped enforcement");
|
|
29961
30879
|
}
|
|
30880
|
+
for (const timer of this.throttleTimers.values()) {
|
|
30881
|
+
clearTimeout(timer);
|
|
30882
|
+
}
|
|
30883
|
+
this.throttleTimers.clear();
|
|
30884
|
+
logger.info("ResourceEnforcer: Stopped enforcement");
|
|
29962
30885
|
}
|
|
29963
30886
|
/**
|
|
29964
30887
|
* Register server for monitoring
|
|
@@ -30108,19 +31031,34 @@ var ResourceEnforcer = class {
|
|
|
30108
31031
|
}
|
|
30109
31032
|
/**
|
|
30110
31033
|
* Throttle process temporarily
|
|
31034
|
+
*
|
|
31035
|
+
* BUG FIX: Now tracks the resume timer so it can be cleaned up on stop().
|
|
31036
|
+
* Also checks if process is still running before attempting to resume.
|
|
30111
31037
|
*/
|
|
30112
31038
|
async throttleProcess(serverProcess) {
|
|
31039
|
+
const serverName = serverProcess.config.name;
|
|
30113
31040
|
try {
|
|
30114
31041
|
logger.info("ResourceEnforcer: Throttling process", {
|
|
30115
|
-
serverName
|
|
31042
|
+
serverName,
|
|
30116
31043
|
pid: serverProcess.process.pid
|
|
30117
31044
|
});
|
|
30118
31045
|
serverProcess.process.kill("SIGSTOP");
|
|
30119
|
-
|
|
31046
|
+
const existingTimer = this.throttleTimers.get(serverName);
|
|
31047
|
+
if (existingTimer) {
|
|
31048
|
+
clearTimeout(existingTimer);
|
|
31049
|
+
}
|
|
31050
|
+
const resumeTimer = setTimeout(() => {
|
|
31051
|
+
this.throttleTimers.delete(serverName);
|
|
31052
|
+
if (!serverProcess.running || serverProcess.process.killed) {
|
|
31053
|
+
logger.debug("ResourceEnforcer: Skipping resume - process no longer running", {
|
|
31054
|
+
serverName
|
|
31055
|
+
});
|
|
31056
|
+
return;
|
|
31057
|
+
}
|
|
30120
31058
|
try {
|
|
30121
31059
|
serverProcess.process.kill("SIGCONT");
|
|
30122
31060
|
logger.info("ResourceEnforcer: Resumed process", {
|
|
30123
|
-
serverName
|
|
31061
|
+
serverName
|
|
30124
31062
|
});
|
|
30125
31063
|
} catch (error) {
|
|
30126
31064
|
logger.error("ResourceEnforcer: Failed to resume process", {
|
|
@@ -30128,6 +31066,7 @@ var ResourceEnforcer = class {
|
|
|
30128
31066
|
});
|
|
30129
31067
|
}
|
|
30130
31068
|
}, 1e3);
|
|
31069
|
+
this.throttleTimers.set(serverName, resumeTimer);
|
|
30131
31070
|
} catch (error) {
|
|
30132
31071
|
logger.error("ResourceEnforcer: Failed to throttle process", {
|
|
30133
31072
|
error: error.message
|
|
@@ -30890,7 +31829,7 @@ var GeminiMCPManager = class {
|
|
|
30890
31829
|
};
|
|
30891
31830
|
var defaultManager = null;
|
|
30892
31831
|
function getDefaultGeminiMCPManager(config) {
|
|
30893
|
-
if (!defaultManager
|
|
31832
|
+
if (!defaultManager) {
|
|
30894
31833
|
defaultManager = new GeminiMCPManager(
|
|
30895
31834
|
{
|
|
30896
31835
|
// Default empty config
|
|
@@ -31402,9 +32341,9 @@ var ProgressIndicator = class {
|
|
|
31402
32341
|
// src/cli/commands/memory.ts
|
|
31403
32342
|
var DEFAULT_DB_PATH = ".automatosx/memory/memory.db";
|
|
31404
32343
|
function getMemoryManager(dbPath) {
|
|
31405
|
-
const
|
|
32344
|
+
const path8 = dbPath || DEFAULT_DB_PATH;
|
|
31406
32345
|
return new LazyMemoryManager({
|
|
31407
|
-
dbPath: resolve(
|
|
32346
|
+
dbPath: resolve(path8),
|
|
31408
32347
|
maxEntries: 1e5,
|
|
31409
32348
|
autoCleanup: false,
|
|
31410
32349
|
trackAccess: true
|
|
@@ -34405,14 +35344,14 @@ var IterateClassifier = class {
|
|
|
34405
35344
|
* **Phase 1 (Week 1)**: Skeleton only (no-op)
|
|
34406
35345
|
* **Phase 2 (Week 2)**: Full implementation with YAML loading and validation
|
|
34407
35346
|
*/
|
|
34408
|
-
async loadPatterns(
|
|
34409
|
-
logger.debug("Loading pattern library", { path:
|
|
34410
|
-
if (!existsSync(
|
|
34411
|
-
logger.warn("Pattern library file not found", { path:
|
|
35347
|
+
async loadPatterns(path8) {
|
|
35348
|
+
logger.debug("Loading pattern library", { path: path8 });
|
|
35349
|
+
if (!existsSync(path8)) {
|
|
35350
|
+
logger.warn("Pattern library file not found", { path: path8 });
|
|
34412
35351
|
return;
|
|
34413
35352
|
}
|
|
34414
35353
|
try {
|
|
34415
|
-
const fileContent = await readFile(
|
|
35354
|
+
const fileContent = await readFile(path8, "utf-8");
|
|
34416
35355
|
const parsed = load(fileContent);
|
|
34417
35356
|
if (!parsed || typeof parsed !== "object") {
|
|
34418
35357
|
throw new Error("Invalid pattern library format: not an object");
|
|
@@ -34455,7 +35394,7 @@ var IterateClassifier = class {
|
|
|
34455
35394
|
});
|
|
34456
35395
|
} catch (error) {
|
|
34457
35396
|
logger.error("Failed to load pattern library", {
|
|
34458
|
-
path:
|
|
35397
|
+
path: path8,
|
|
34459
35398
|
error: error.message
|
|
34460
35399
|
});
|
|
34461
35400
|
throw error;
|
|
@@ -34612,7 +35551,8 @@ var IterateClassifier = class {
|
|
|
34612
35551
|
checkProviderMarkers(message, provider) {
|
|
34613
35552
|
const providerLower = provider.toLowerCase();
|
|
34614
35553
|
if (providerLower.includes("claude")) {
|
|
34615
|
-
|
|
35554
|
+
const lowerMessage = message.toLowerCase();
|
|
35555
|
+
if (lowerMessage.includes("<thinking>") || lowerMessage.includes("</thinking>") || lowerMessage.includes("<thought>") || lowerMessage.includes("</thought>")) {
|
|
34616
35556
|
return { type: "status_update", confidence: 0.85 };
|
|
34617
35557
|
}
|
|
34618
35558
|
if (message.includes("Let me break this") || message.includes("I'll break this")) {
|
|
@@ -34809,14 +35749,14 @@ var IterateAutoResponder = class {
|
|
|
34809
35749
|
* **Phase 1 (Week 1)**: Skeleton only (no-op)
|
|
34810
35750
|
* **Phase 3 (Week 3)**: Full implementation with YAML loading and validation
|
|
34811
35751
|
*/
|
|
34812
|
-
async loadTemplates(
|
|
34813
|
-
logger.debug("Loading template library", { path:
|
|
34814
|
-
if (!existsSync(
|
|
34815
|
-
logger.warn("Template library file not found", { path:
|
|
35752
|
+
async loadTemplates(path8) {
|
|
35753
|
+
logger.debug("Loading template library", { path: path8 });
|
|
35754
|
+
if (!existsSync(path8)) {
|
|
35755
|
+
logger.warn("Template library file not found", { path: path8 });
|
|
34816
35756
|
return;
|
|
34817
35757
|
}
|
|
34818
35758
|
try {
|
|
34819
|
-
const fileContent = await readFile(
|
|
35759
|
+
const fileContent = await readFile(path8, "utf-8");
|
|
34820
35760
|
const parsed = load(fileContent);
|
|
34821
35761
|
if (!parsed || typeof parsed !== "object") {
|
|
34822
35762
|
throw new Error("Invalid template library format: not an object");
|
|
@@ -34858,7 +35798,7 @@ var IterateAutoResponder = class {
|
|
|
34858
35798
|
});
|
|
34859
35799
|
} catch (error) {
|
|
34860
35800
|
logger.error("Failed to load template library", {
|
|
34861
|
-
path:
|
|
35801
|
+
path: path8,
|
|
34862
35802
|
error: error.message
|
|
34863
35803
|
});
|
|
34864
35804
|
throw error;
|
|
@@ -35022,6 +35962,16 @@ var IterateModeController = class {
|
|
|
35022
35962
|
* @param config - Iterate mode configuration
|
|
35023
35963
|
* @param sessionManager - Session manager for state persistence (optional)
|
|
35024
35964
|
*/
|
|
35965
|
+
/**
|
|
35966
|
+
* Flag indicating if patterns/templates have been loaded
|
|
35967
|
+
* @private
|
|
35968
|
+
*/
|
|
35969
|
+
initialized = false;
|
|
35970
|
+
/**
|
|
35971
|
+
* Promise for initialization (to avoid race conditions)
|
|
35972
|
+
* @private
|
|
35973
|
+
*/
|
|
35974
|
+
initPromise;
|
|
35025
35975
|
constructor(config, sessionManager, statusRenderer) {
|
|
35026
35976
|
this.config = config;
|
|
35027
35977
|
this.sessionManager = sessionManager;
|
|
@@ -35039,6 +35989,58 @@ var IterateModeController = class {
|
|
|
35039
35989
|
strictness: config.classifier.strictness,
|
|
35040
35990
|
hasStatusRenderer: !!statusRenderer
|
|
35041
35991
|
});
|
|
35992
|
+
this.initPromise = this.loadPatternsAndTemplates();
|
|
35993
|
+
}
|
|
35994
|
+
/**
|
|
35995
|
+
* Load pattern and template libraries
|
|
35996
|
+
*
|
|
35997
|
+
* Automatically loads patterns.yaml and templates.yaml from the configured paths.
|
|
35998
|
+
* If files don't exist, uses fallback patterns.
|
|
35999
|
+
*
|
|
36000
|
+
* @private
|
|
36001
|
+
*/
|
|
36002
|
+
async loadPatternsAndTemplates() {
|
|
36003
|
+
if (this.initialized) {
|
|
36004
|
+
return;
|
|
36005
|
+
}
|
|
36006
|
+
const patternsPath = this.config.classifier.patternLibraryPath;
|
|
36007
|
+
const templatesPath = patternsPath.replace("patterns.yaml", "templates.yaml");
|
|
36008
|
+
if (existsSync(patternsPath)) {
|
|
36009
|
+
try {
|
|
36010
|
+
await this.classifier.loadPatterns(patternsPath);
|
|
36011
|
+
logger.info("Loaded iterate patterns", { path: patternsPath });
|
|
36012
|
+
} catch (error) {
|
|
36013
|
+
logger.warn("Failed to load iterate patterns, using defaults", {
|
|
36014
|
+
path: patternsPath,
|
|
36015
|
+
error: error.message
|
|
36016
|
+
});
|
|
36017
|
+
}
|
|
36018
|
+
} else {
|
|
36019
|
+
logger.warn("Iterate patterns file not found, using defaults", { path: patternsPath });
|
|
36020
|
+
}
|
|
36021
|
+
if (existsSync(templatesPath)) {
|
|
36022
|
+
try {
|
|
36023
|
+
await this.responder.loadTemplates(templatesPath);
|
|
36024
|
+
logger.info("Loaded iterate templates", { path: templatesPath });
|
|
36025
|
+
} catch (error) {
|
|
36026
|
+
logger.warn("Failed to load iterate templates, using defaults", {
|
|
36027
|
+
path: templatesPath,
|
|
36028
|
+
error: error.message
|
|
36029
|
+
});
|
|
36030
|
+
}
|
|
36031
|
+
} else {
|
|
36032
|
+
logger.warn("Iterate templates file not found, using defaults", { path: templatesPath });
|
|
36033
|
+
}
|
|
36034
|
+
this.initialized = true;
|
|
36035
|
+
}
|
|
36036
|
+
/**
|
|
36037
|
+
* Ensure patterns and templates are loaded before use
|
|
36038
|
+
* @private
|
|
36039
|
+
*/
|
|
36040
|
+
async ensureInitialized() {
|
|
36041
|
+
if (!this.initialized && this.initPromise) {
|
|
36042
|
+
await this.initPromise;
|
|
36043
|
+
}
|
|
35042
36044
|
}
|
|
35043
36045
|
/**
|
|
35044
36046
|
* Execute agent with iterate mode enabled
|
|
@@ -35057,6 +36059,7 @@ var IterateModeController = class {
|
|
|
35057
36059
|
* **Phase 3 (Week 3)**: Full implementation with orchestration loop
|
|
35058
36060
|
*/
|
|
35059
36061
|
async executeWithIterate(context, options) {
|
|
36062
|
+
await this.ensureInitialized();
|
|
35060
36063
|
const sessionId = this.sessionManager ? (await this.sessionManager.createSession(context.task, context.agent.name)).id : randomUUID();
|
|
35061
36064
|
this.initializeState(sessionId);
|
|
35062
36065
|
logger.info("Iterate mode execution started", {
|
|
@@ -35159,17 +36162,16 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
35159
36162
|
*/
|
|
35160
36163
|
async handleResponse(response) {
|
|
35161
36164
|
const startTime = Date.now();
|
|
36165
|
+
await this.ensureInitialized();
|
|
35162
36166
|
if (!this.state) {
|
|
35163
|
-
logger.warn("handleResponse called without initialized state -
|
|
35164
|
-
|
|
35165
|
-
type: "continue",
|
|
35166
|
-
reason: "Skeleton implementation - state not initialized"
|
|
35167
|
-
};
|
|
36167
|
+
logger.warn("handleResponse called without initialized state - initializing now");
|
|
36168
|
+
this.initializeState(randomUUID());
|
|
35168
36169
|
}
|
|
36170
|
+
const state = this.state;
|
|
35169
36171
|
logger.debug("Handling response", {
|
|
35170
36172
|
hasContent: !!response.content,
|
|
35171
36173
|
contentLength: response.content?.length || 0,
|
|
35172
|
-
iterations:
|
|
36174
|
+
iterations: state.totalIterations
|
|
35173
36175
|
});
|
|
35174
36176
|
const classification = await this.classifier.classify(
|
|
35175
36177
|
response.content || "",
|
|
@@ -35180,12 +36182,12 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
35180
36182
|
tokenCount: response.tokensUsed?.total
|
|
35181
36183
|
}
|
|
35182
36184
|
);
|
|
35183
|
-
|
|
36185
|
+
state.classificationHistory.push(classification);
|
|
35184
36186
|
const maxHistorySize = this.config.classifier.contextWindowMessages * 2;
|
|
35185
|
-
if (
|
|
35186
|
-
|
|
36187
|
+
if (state.classificationHistory.length > maxHistorySize) {
|
|
36188
|
+
state.classificationHistory = state.classificationHistory.slice(-maxHistorySize);
|
|
35187
36189
|
logger.debug("Trimmed classification history to prevent memory leak", {
|
|
35188
|
-
newSize:
|
|
36190
|
+
newSize: state.classificationHistory.length,
|
|
35189
36191
|
maxSize: maxHistorySize
|
|
35190
36192
|
});
|
|
35191
36193
|
}
|
|
@@ -35229,8 +36231,18 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
35229
36231
|
context: `Maximum iterations reached`
|
|
35230
36232
|
};
|
|
35231
36233
|
}
|
|
36234
|
+
const safetyCheck = this.checkSafetyGuards(response.content || "");
|
|
36235
|
+
if (!safetyCheck.safe && this.config.notifications.pauseOnHighRiskOperation) {
|
|
36236
|
+
const dangerousOps = safetyCheck.detectedOperations.map((op) => `${op.type}: ${op.match}`).join(", ");
|
|
36237
|
+
return {
|
|
36238
|
+
type: "pause",
|
|
36239
|
+
reason: "Dangerous operation detected",
|
|
36240
|
+
pauseReason: "high_risk_operation",
|
|
36241
|
+
context: `Detected: ${dangerousOps}`
|
|
36242
|
+
};
|
|
36243
|
+
}
|
|
35232
36244
|
const action = this.determineAction(classification, response);
|
|
35233
|
-
if (action.type === "continue" && action.response === void 0) {
|
|
36245
|
+
if ((action.type === "continue" || action.type === "stop") && action.response === void 0) {
|
|
35234
36246
|
const autoResponse = await this.responder.generateResponse(
|
|
35235
36247
|
classification,
|
|
35236
36248
|
{
|
|
@@ -35258,7 +36270,7 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
35258
36270
|
this.emitEvent({
|
|
35259
36271
|
type: "iterate.classification",
|
|
35260
36272
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35261
|
-
sessionId:
|
|
36273
|
+
sessionId: state.sessionId,
|
|
35262
36274
|
payload: {
|
|
35263
36275
|
classification,
|
|
35264
36276
|
action: action.type,
|
|
@@ -35317,8 +36329,8 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
35317
36329
|
}
|
|
35318
36330
|
if (type === "completion_signal") {
|
|
35319
36331
|
return {
|
|
35320
|
-
type: "
|
|
35321
|
-
reason: "Completion signal -
|
|
36332
|
+
type: "stop",
|
|
36333
|
+
reason: "Completion signal - task finished",
|
|
35322
36334
|
metadata: { confidence }
|
|
35323
36335
|
};
|
|
35324
36336
|
}
|
|
@@ -35538,9 +36550,8 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
35538
36550
|
// v8.5.8 - Deprecated, always 0 (cost tracking removed)
|
|
35539
36551
|
classificationBreakdown: breakdown,
|
|
35540
36552
|
avgClassificationLatencyMs: Math.round(avgLatency),
|
|
35541
|
-
safetyChecks: {
|
|
36553
|
+
safetyChecks: this.state.metadata.safetyChecks || {
|
|
35542
36554
|
total: 0,
|
|
35543
|
-
// TODO: Track safety checks
|
|
35544
36555
|
allowed: 0,
|
|
35545
36556
|
paused: 0,
|
|
35546
36557
|
blocked: 0
|
|
@@ -35552,19 +36563,42 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
35552
36563
|
/**
|
|
35553
36564
|
* Emit telemetry event
|
|
35554
36565
|
*
|
|
36566
|
+
* Writes events to JSONL telemetry file for observability.
|
|
36567
|
+
*
|
|
35555
36568
|
* @param event - Telemetry event to emit
|
|
35556
36569
|
* @private
|
|
35557
|
-
*
|
|
35558
|
-
* **Phase 1 (Week 1)**: Logs only
|
|
35559
|
-
* **Phase 4 (Week 4)**: Full telemetry with file logging
|
|
35560
36570
|
*/
|
|
35561
36571
|
emitEvent(event) {
|
|
35562
|
-
if (this.config.telemetry.emitMetrics) {
|
|
35563
|
-
|
|
35564
|
-
|
|
35565
|
-
|
|
35566
|
-
|
|
36572
|
+
if (!this.config.telemetry.emitMetrics) {
|
|
36573
|
+
return;
|
|
36574
|
+
}
|
|
36575
|
+
logger.debug("Iterate event", {
|
|
36576
|
+
type: event.type,
|
|
36577
|
+
sessionId: event.sessionId
|
|
36578
|
+
});
|
|
36579
|
+
const patternsPath = this.config.classifier.patternLibraryPath;
|
|
36580
|
+
const iterateDir = dirname(patternsPath);
|
|
36581
|
+
const baseDir = dirname(iterateDir);
|
|
36582
|
+
const logDir = join(baseDir, "logs");
|
|
36583
|
+
const logPath = join(logDir, `iterate-trace-${event.sessionId}.jsonl`);
|
|
36584
|
+
const line = JSON.stringify(event) + "\n";
|
|
36585
|
+
if (!existsSync(logDir)) {
|
|
36586
|
+
try {
|
|
36587
|
+
mkdirSync(logDir, { recursive: true });
|
|
36588
|
+
} catch {
|
|
36589
|
+
}
|
|
35567
36590
|
}
|
|
36591
|
+
appendFile(logPath, line).catch((error) => {
|
|
36592
|
+
if (!this.state?.metadata.telemetryErrorLogged) {
|
|
36593
|
+
logger.warn("Failed to write iterate telemetry", {
|
|
36594
|
+
path: logPath,
|
|
36595
|
+
error: error.message
|
|
36596
|
+
});
|
|
36597
|
+
if (this.state) {
|
|
36598
|
+
this.state.metadata.telemetryErrorLogged = true;
|
|
36599
|
+
}
|
|
36600
|
+
}
|
|
36601
|
+
});
|
|
35568
36602
|
}
|
|
35569
36603
|
/**
|
|
35570
36604
|
* Check if time budget exceeded
|
|
@@ -35771,6 +36805,175 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
35771
36805
|
timestamp: c.timestamp
|
|
35772
36806
|
}));
|
|
35773
36807
|
}
|
|
36808
|
+
/**
|
|
36809
|
+
* Check for dangerous operations in response content
|
|
36810
|
+
*
|
|
36811
|
+
* Detects potentially destructive commands and operations that should
|
|
36812
|
+
* pause execution for user confirmation.
|
|
36813
|
+
*
|
|
36814
|
+
* @param content - Response content to check
|
|
36815
|
+
* @returns Safety check result with detected operations
|
|
36816
|
+
*/
|
|
36817
|
+
checkSafetyGuards(content) {
|
|
36818
|
+
if (!this.config.safety.enableDangerousOperationGuard) {
|
|
36819
|
+
return { safe: true, detectedOperations: [] };
|
|
36820
|
+
}
|
|
36821
|
+
const detectedOperations = [];
|
|
36822
|
+
const dangerousPatterns = [
|
|
36823
|
+
// File deletion (HIGH risk)
|
|
36824
|
+
{
|
|
36825
|
+
pattern: /\brm\s+-rf\s+[\/~]/i,
|
|
36826
|
+
type: "fileDelete",
|
|
36827
|
+
risk: "HIGH"
|
|
36828
|
+
},
|
|
36829
|
+
{
|
|
36830
|
+
pattern: /\brm\s+-rf\s+\./i,
|
|
36831
|
+
type: "fileDelete",
|
|
36832
|
+
risk: "HIGH"
|
|
36833
|
+
},
|
|
36834
|
+
{
|
|
36835
|
+
pattern: /\brmdir\s+.*--ignore-fail-on-non-empty/i,
|
|
36836
|
+
type: "fileDelete",
|
|
36837
|
+
risk: "MEDIUM"
|
|
36838
|
+
},
|
|
36839
|
+
// Git force operations (HIGH risk)
|
|
36840
|
+
{
|
|
36841
|
+
pattern: /\bgit\s+push\s+.*--force/i,
|
|
36842
|
+
type: "gitForce",
|
|
36843
|
+
risk: "HIGH"
|
|
36844
|
+
},
|
|
36845
|
+
{
|
|
36846
|
+
pattern: /\bgit\s+push\s+-f\b/i,
|
|
36847
|
+
type: "gitForce",
|
|
36848
|
+
risk: "HIGH"
|
|
36849
|
+
},
|
|
36850
|
+
{
|
|
36851
|
+
pattern: /\bgit\s+reset\s+--hard/i,
|
|
36852
|
+
type: "gitForce",
|
|
36853
|
+
risk: "HIGH"
|
|
36854
|
+
},
|
|
36855
|
+
{
|
|
36856
|
+
pattern: /\bgit\s+clean\s+-fd/i,
|
|
36857
|
+
type: "gitForce",
|
|
36858
|
+
risk: "MEDIUM"
|
|
36859
|
+
},
|
|
36860
|
+
// Database operations (HIGH risk)
|
|
36861
|
+
{
|
|
36862
|
+
pattern: /\bDROP\s+(TABLE|DATABASE|SCHEMA)\b/i,
|
|
36863
|
+
type: "databaseDrop",
|
|
36864
|
+
risk: "HIGH"
|
|
36865
|
+
},
|
|
36866
|
+
{
|
|
36867
|
+
pattern: /\bTRUNCATE\s+TABLE\b/i,
|
|
36868
|
+
type: "databaseTruncate",
|
|
36869
|
+
risk: "HIGH"
|
|
36870
|
+
},
|
|
36871
|
+
{
|
|
36872
|
+
pattern: /\bDELETE\s+FROM\s+\w+\s*(;|$|WHERE\s+1\s*=\s*1)/i,
|
|
36873
|
+
type: "databaseDelete",
|
|
36874
|
+
risk: "HIGH"
|
|
36875
|
+
},
|
|
36876
|
+
// Secrets/credentials in code (HIGH risk)
|
|
36877
|
+
{
|
|
36878
|
+
pattern: /\b(password|secret|api_key|apikey|api-key|token)\s*[=:]\s*["'][^"']{8,}["']/i,
|
|
36879
|
+
type: "secretsInCode",
|
|
36880
|
+
risk: "HIGH"
|
|
36881
|
+
},
|
|
36882
|
+
{
|
|
36883
|
+
pattern: /\b(AWS_SECRET|PRIVATE_KEY|AUTH_TOKEN)\s*[=:]/i,
|
|
36884
|
+
type: "secretsInCode",
|
|
36885
|
+
risk: "HIGH"
|
|
36886
|
+
},
|
|
36887
|
+
// Shell commands (MEDIUM risk based on config)
|
|
36888
|
+
{
|
|
36889
|
+
pattern: /\bsudo\s+/i,
|
|
36890
|
+
type: "shellCommands",
|
|
36891
|
+
risk: "MEDIUM"
|
|
36892
|
+
},
|
|
36893
|
+
{
|
|
36894
|
+
pattern: /\bchmod\s+777\b/i,
|
|
36895
|
+
type: "shellCommands",
|
|
36896
|
+
risk: "MEDIUM"
|
|
36897
|
+
},
|
|
36898
|
+
{
|
|
36899
|
+
pattern: /\bcurl\s+.*\|\s*(bash|sh)\b/i,
|
|
36900
|
+
type: "shellCommands",
|
|
36901
|
+
risk: "HIGH"
|
|
36902
|
+
},
|
|
36903
|
+
{
|
|
36904
|
+
pattern: /\bwget\s+.*\|\s*(bash|sh)\b/i,
|
|
36905
|
+
type: "shellCommands",
|
|
36906
|
+
risk: "HIGH"
|
|
36907
|
+
},
|
|
36908
|
+
// Package installation (MEDIUM risk)
|
|
36909
|
+
{
|
|
36910
|
+
pattern: /\bnpm\s+install\s+--global\b/i,
|
|
36911
|
+
type: "packageInstall",
|
|
36912
|
+
risk: "MEDIUM"
|
|
36913
|
+
},
|
|
36914
|
+
{
|
|
36915
|
+
pattern: /\bpip\s+install\s+--user\b/i,
|
|
36916
|
+
type: "packageInstall",
|
|
36917
|
+
risk: "LOW"
|
|
36918
|
+
},
|
|
36919
|
+
// Writing outside workspace (HIGH risk)
|
|
36920
|
+
{
|
|
36921
|
+
pattern: /\b(write|create|save)\s+.*[\/~](etc|usr|bin|lib|opt)\//i,
|
|
36922
|
+
type: "writeOutsideWorkspace",
|
|
36923
|
+
risk: "HIGH"
|
|
36924
|
+
},
|
|
36925
|
+
{
|
|
36926
|
+
pattern: /\becho\s+.*>\s*[\/~](etc|usr|bin)/i,
|
|
36927
|
+
type: "writeOutsideWorkspace",
|
|
36928
|
+
risk: "HIGH"
|
|
36929
|
+
}
|
|
36930
|
+
];
|
|
36931
|
+
for (const { pattern, type, risk } of dangerousPatterns) {
|
|
36932
|
+
const match = content.match(pattern);
|
|
36933
|
+
if (match) {
|
|
36934
|
+
const configuredRisk = this.config.safety.dangerousOperations[type];
|
|
36935
|
+
if (configuredRisk) {
|
|
36936
|
+
detectedOperations.push({
|
|
36937
|
+
type,
|
|
36938
|
+
risk,
|
|
36939
|
+
match: match[0].substring(0, 100)
|
|
36940
|
+
// Truncate long matches
|
|
36941
|
+
});
|
|
36942
|
+
}
|
|
36943
|
+
}
|
|
36944
|
+
}
|
|
36945
|
+
const riskTolerance = this.config.safety.riskTolerance;
|
|
36946
|
+
const isSafe = detectedOperations.every((op) => {
|
|
36947
|
+
if (riskTolerance === "permissive") {
|
|
36948
|
+
return op.risk !== "HIGH";
|
|
36949
|
+
} else if (riskTolerance === "balanced") {
|
|
36950
|
+
return op.risk === "LOW";
|
|
36951
|
+
} else {
|
|
36952
|
+
return false;
|
|
36953
|
+
}
|
|
36954
|
+
});
|
|
36955
|
+
if (detectedOperations.length > 0) {
|
|
36956
|
+
logger.warn("Dangerous operations detected", {
|
|
36957
|
+
count: detectedOperations.length,
|
|
36958
|
+
operations: detectedOperations,
|
|
36959
|
+
safe: isSafe,
|
|
36960
|
+
riskTolerance
|
|
36961
|
+
});
|
|
36962
|
+
if (this.state) {
|
|
36963
|
+
this.state.metadata.safetyChecks = this.state.metadata.safetyChecks || { total: 0, allowed: 0, paused: 0, blocked: 0 };
|
|
36964
|
+
this.state.metadata.safetyChecks.total++;
|
|
36965
|
+
if (isSafe) {
|
|
36966
|
+
this.state.metadata.safetyChecks.allowed++;
|
|
36967
|
+
} else {
|
|
36968
|
+
this.state.metadata.safetyChecks.paused++;
|
|
36969
|
+
}
|
|
36970
|
+
}
|
|
36971
|
+
}
|
|
36972
|
+
return {
|
|
36973
|
+
safe: isSafe || detectedOperations.length === 0,
|
|
36974
|
+
detectedOperations
|
|
36975
|
+
};
|
|
36976
|
+
}
|
|
35774
36977
|
};
|
|
35775
36978
|
|
|
35776
36979
|
// src/cli/renderers/iterate-status-renderer.ts
|
|
@@ -37538,7 +38741,10 @@ Result: ${result.stages.map((s) => s.output).join("\n\n")}`;
|
|
|
37538
38741
|
writeOutsideWorkspace: "HIGH",
|
|
37539
38742
|
secretsInCode: "HIGH",
|
|
37540
38743
|
shellCommands: "MEDIUM",
|
|
37541
|
-
packageInstall: "MEDIUM"
|
|
38744
|
+
packageInstall: "MEDIUM",
|
|
38745
|
+
databaseDrop: "HIGH",
|
|
38746
|
+
databaseTruncate: "HIGH",
|
|
38747
|
+
databaseDelete: "HIGH"
|
|
37542
38748
|
},
|
|
37543
38749
|
enableTimeTracking: true,
|
|
37544
38750
|
enableIterationTracking: true
|
|
@@ -38670,11 +39876,11 @@ async function getCurrentVersion() {
|
|
|
38670
39876
|
return result.dependencies["@defai.digital/automatosx"]?.version || "unknown";
|
|
38671
39877
|
} catch (error) {
|
|
38672
39878
|
const { readFile: readFile24 } = await import('fs/promises');
|
|
38673
|
-
const { dirname:
|
|
39879
|
+
const { dirname: dirname16, join: join52 } = await import('path');
|
|
38674
39880
|
const { fileURLToPath: fileURLToPath5 } = await import('url');
|
|
38675
39881
|
const __filename3 = fileURLToPath5(import.meta.url);
|
|
38676
|
-
const __dirname4 =
|
|
38677
|
-
const pkgPath =
|
|
39882
|
+
const __dirname4 = dirname16(__filename3);
|
|
39883
|
+
const pkgPath = join52(__dirname4, "../../../package.json");
|
|
38678
39884
|
const content = await readFile24(pkgPath, "utf-8");
|
|
38679
39885
|
const pkg = JSON.parse(content);
|
|
38680
39886
|
return pkg.version;
|
|
@@ -40744,8 +41950,8 @@ var ConfigManager = class {
|
|
|
40744
41950
|
* @returns User configuration or empty object if not found
|
|
40745
41951
|
*/
|
|
40746
41952
|
async readUserConfig() {
|
|
40747
|
-
const
|
|
40748
|
-
return this.readConfig(
|
|
41953
|
+
const path8 = getUserConfigPath();
|
|
41954
|
+
return this.readConfig(path8, "user");
|
|
40749
41955
|
}
|
|
40750
41956
|
/**
|
|
40751
41957
|
* Read project-level Gemini CLI configuration
|
|
@@ -40753,8 +41959,8 @@ var ConfigManager = class {
|
|
|
40753
41959
|
* @returns Project configuration or empty object if not found
|
|
40754
41960
|
*/
|
|
40755
41961
|
async readProjectConfig() {
|
|
40756
|
-
const
|
|
40757
|
-
return this.readConfig(
|
|
41962
|
+
const path8 = getProjectConfigPath();
|
|
41963
|
+
return this.readConfig(path8, "project");
|
|
40758
41964
|
}
|
|
40759
41965
|
/**
|
|
40760
41966
|
* Read configuration from a specific path with caching
|
|
@@ -40764,7 +41970,7 @@ var ConfigManager = class {
|
|
|
40764
41970
|
* @returns Configuration object
|
|
40765
41971
|
* @private
|
|
40766
41972
|
*/
|
|
40767
|
-
async readConfig(
|
|
41973
|
+
async readConfig(path8, scope) {
|
|
40768
41974
|
const cached = this.cache.get(scope);
|
|
40769
41975
|
if (cached && Date.now() - cached.timestamp < this.ttl) {
|
|
40770
41976
|
return cached.data;
|
|
@@ -40775,13 +41981,13 @@ var ConfigManager = class {
|
|
|
40775
41981
|
}
|
|
40776
41982
|
const readOperation = (async () => {
|
|
40777
41983
|
try {
|
|
40778
|
-
const exists = await fileExists(
|
|
41984
|
+
const exists = await fileExists(path8);
|
|
40779
41985
|
if (!exists) {
|
|
40780
41986
|
const emptyConfig = {};
|
|
40781
41987
|
this.cache.set(scope, { data: emptyConfig, timestamp: Date.now() });
|
|
40782
41988
|
return emptyConfig;
|
|
40783
41989
|
}
|
|
40784
|
-
const config = await readJsonFile(
|
|
41990
|
+
const config = await readJsonFile(path8);
|
|
40785
41991
|
this.cache.set(scope, { data: config, timestamp: Date.now() });
|
|
40786
41992
|
return config;
|
|
40787
41993
|
} catch (error) {
|
|
@@ -40790,8 +41996,8 @@ var ConfigManager = class {
|
|
|
40790
41996
|
}
|
|
40791
41997
|
throw new GeminiCLIError(
|
|
40792
41998
|
"INVALID_CONFIG" /* INVALID_CONFIG */,
|
|
40793
|
-
`Failed to read configuration from ${
|
|
40794
|
-
{ path:
|
|
41999
|
+
`Failed to read configuration from ${path8}`,
|
|
42000
|
+
{ path: path8, originalError: error }
|
|
40795
42001
|
);
|
|
40796
42002
|
} finally {
|
|
40797
42003
|
this.pendingReads.delete(scope);
|
|
@@ -41461,8 +42667,8 @@ var CommandTranslator = class {
|
|
|
41461
42667
|
paths.push(getProjectCommandsPath());
|
|
41462
42668
|
}
|
|
41463
42669
|
}
|
|
41464
|
-
for (const
|
|
41465
|
-
const discovered = await this.scanDirectory(
|
|
42670
|
+
for (const path8 of paths) {
|
|
42671
|
+
const discovered = await this.scanDirectory(path8);
|
|
41466
42672
|
commands.push(...discovered);
|
|
41467
42673
|
}
|
|
41468
42674
|
return commands;
|
|
@@ -41503,8 +42709,8 @@ var CommandTranslator = class {
|
|
|
41503
42709
|
const results = [];
|
|
41504
42710
|
for (const name of commandNames) {
|
|
41505
42711
|
try {
|
|
41506
|
-
const
|
|
41507
|
-
results.push(
|
|
42712
|
+
const path8 = await this.importCommand(name, outputDir, options);
|
|
42713
|
+
results.push(path8);
|
|
41508
42714
|
} catch (error) {
|
|
41509
42715
|
if (error instanceof GeminiCLIError) {
|
|
41510
42716
|
console.error(`Failed to import ${name}: ${error.message}`);
|
|
@@ -42836,7 +44042,7 @@ var SpecSchemaValidator = class {
|
|
|
42836
44042
|
* Convert Ajv error to ValidationIssue
|
|
42837
44043
|
*/
|
|
42838
44044
|
convertAjvError(error) {
|
|
42839
|
-
const
|
|
44045
|
+
const path8 = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
|
|
42840
44046
|
const keyword = error.keyword;
|
|
42841
44047
|
let message = error.message || "Validation error";
|
|
42842
44048
|
let suggestion;
|
|
@@ -42887,7 +44093,7 @@ var SpecSchemaValidator = class {
|
|
|
42887
44093
|
ruleId: `SCHEMA-${keyword.toUpperCase()}`,
|
|
42888
44094
|
severity: "error",
|
|
42889
44095
|
message,
|
|
42890
|
-
path:
|
|
44096
|
+
path: path8 || void 0,
|
|
42891
44097
|
suggestion
|
|
42892
44098
|
};
|
|
42893
44099
|
}
|
|
@@ -45706,10 +46912,10 @@ async function handleReset() {
|
|
|
45706
46912
|
`));
|
|
45707
46913
|
}
|
|
45708
46914
|
async function handleTrace(workspacePath, argv) {
|
|
45709
|
-
const { existsSync:
|
|
45710
|
-
const { join:
|
|
45711
|
-
const traceFile =
|
|
45712
|
-
if (!
|
|
46915
|
+
const { existsSync: existsSync27, readFileSync: readFileSync9, watchFile } = await import('fs');
|
|
46916
|
+
const { join: join52 } = await import('path');
|
|
46917
|
+
const traceFile = join52(workspacePath, ".automatosx/logs/router.trace.jsonl");
|
|
46918
|
+
if (!existsSync27(traceFile)) {
|
|
45713
46919
|
console.log(chalk5.yellow("\n\u26A0\uFE0F No trace log found\n"));
|
|
45714
46920
|
console.log(chalk5.gray(`Expected location: ${traceFile}
|
|
45715
46921
|
`));
|
|
@@ -48617,13 +49823,13 @@ Time since last change: ${timeSinceLastChange}ms`
|
|
|
48617
49823
|
init_logger();
|
|
48618
49824
|
var flagManagerInstances = /* @__PURE__ */ new Map();
|
|
48619
49825
|
function getFlagManager(workspacePath) {
|
|
48620
|
-
const
|
|
48621
|
-
if (!flagManagerInstances.has(
|
|
48622
|
-
const manager = new FeatureFlagManager(
|
|
49826
|
+
const path8 = process.cwd();
|
|
49827
|
+
if (!flagManagerInstances.has(path8)) {
|
|
49828
|
+
const manager = new FeatureFlagManager(path8);
|
|
48623
49829
|
initializeFlags(manager);
|
|
48624
|
-
flagManagerInstances.set(
|
|
49830
|
+
flagManagerInstances.set(path8, manager);
|
|
48625
49831
|
}
|
|
48626
|
-
return flagManagerInstances.get(
|
|
49832
|
+
return flagManagerInstances.get(path8);
|
|
48627
49833
|
}
|
|
48628
49834
|
function initializeFlags(manager) {
|
|
48629
49835
|
if (!manager.hasStorage()) {
|
|
@@ -48944,9 +50150,9 @@ var cliCommand = {
|
|
|
48944
50150
|
// src/cli/commands/uninstall.ts
|
|
48945
50151
|
init_esm_shims();
|
|
48946
50152
|
init_logger();
|
|
48947
|
-
async function fileExists2(
|
|
50153
|
+
async function fileExists2(path8) {
|
|
48948
50154
|
try {
|
|
48949
|
-
await access$1(
|
|
50155
|
+
await access$1(path8);
|
|
48950
50156
|
return true;
|
|
48951
50157
|
} catch {
|
|
48952
50158
|
return false;
|
|
@@ -49519,7 +50725,6 @@ var OrchestrationInstructionInjector = class {
|
|
|
49519
50725
|
providers = /* @__PURE__ */ new Map();
|
|
49520
50726
|
budgetManager;
|
|
49521
50727
|
config;
|
|
49522
|
-
lastInjectionTime = 0;
|
|
49523
50728
|
constructor(config) {
|
|
49524
50729
|
this.config = {
|
|
49525
50730
|
...DEFAULT_ORCHESTRATION_CONFIG,
|
|
@@ -49612,7 +50817,6 @@ var OrchestrationInstructionInjector = class {
|
|
|
49612
50817
|
allocation = this.budgetManager.allocateBudget(activeInstructions);
|
|
49613
50818
|
}
|
|
49614
50819
|
const formattedText = this.formatInstructions(allocation.included);
|
|
49615
|
-
this.lastInjectionTime = Date.now();
|
|
49616
50820
|
const duration = Date.now() - startTime;
|
|
49617
50821
|
logger.debug("Instruction injection complete", {
|
|
49618
50822
|
providers: this.providers.size,
|
|
@@ -50226,7 +51430,7 @@ var SessionInstructionProvider = class {
|
|
|
50226
51430
|
if (!this.config.enabled) {
|
|
50227
51431
|
return false;
|
|
50228
51432
|
}
|
|
50229
|
-
if (!context.sessionId && !this.stateProvider) {
|
|
51433
|
+
if (!context.sessionId && !this.stateProvider && !context.parentAgent) {
|
|
50230
51434
|
return false;
|
|
50231
51435
|
}
|
|
50232
51436
|
const turnsSinceReminder = context.turnCount - this.lastReminderTurn;
|
|
@@ -50238,7 +51442,7 @@ var SessionInstructionProvider = class {
|
|
|
50238
51442
|
async getInstructions(context) {
|
|
50239
51443
|
const instructions = [];
|
|
50240
51444
|
const state = await this.getSessionState(context);
|
|
50241
|
-
if (!state && !context.sessionId) {
|
|
51445
|
+
if (!state && !context.sessionId && !context.parentAgent) {
|
|
50242
51446
|
return instructions;
|
|
50243
51447
|
}
|
|
50244
51448
|
const content = this.formatSessionContext(context, state);
|
|
@@ -51190,7 +52394,6 @@ var AgentInstructionInjector = class {
|
|
|
51190
52394
|
var OrchestrationService = class {
|
|
51191
52395
|
config;
|
|
51192
52396
|
injector;
|
|
51193
|
-
tokenBudgetManager;
|
|
51194
52397
|
workflowModeManager;
|
|
51195
52398
|
agentInjector;
|
|
51196
52399
|
todoProvider;
|
|
@@ -51204,7 +52407,6 @@ var OrchestrationService = class {
|
|
|
51204
52407
|
...serviceConfig
|
|
51205
52408
|
};
|
|
51206
52409
|
this.injector = new OrchestrationInstructionInjector(this.config);
|
|
51207
|
-
this.tokenBudgetManager = new TokenBudgetManager(this.config.tokenBudget);
|
|
51208
52410
|
this.workflowModeManager = new WorkflowModeManager();
|
|
51209
52411
|
this.agentInjector = new AgentInstructionInjector({
|
|
51210
52412
|
enabled: this.config.agentTemplates?.enabled ?? true,
|
|
@@ -51381,7 +52583,7 @@ ${content}
|
|
|
51381
52583
|
* Get debug information about current orchestration state
|
|
51382
52584
|
*/
|
|
51383
52585
|
getDebugInfo() {
|
|
51384
|
-
const budgetConfig = this.
|
|
52586
|
+
const budgetConfig = this.injector.getBudgetManager().getConfig();
|
|
51385
52587
|
const providers = this.injector.getProviders().map((p) => p.name);
|
|
51386
52588
|
return {
|
|
51387
52589
|
turnCount: this.turnCount,
|
|
@@ -51429,9 +52631,6 @@ ${content}
|
|
|
51429
52631
|
sessionIntegration: updates.sessionIntegration ? { ...this.config.sessionIntegration, ...updates.sessionIntegration } : this.config.sessionIntegration,
|
|
51430
52632
|
agentTemplates: updates.agentTemplates ? { ...this.config.agentTemplates, ...updates.agentTemplates } : this.config.agentTemplates
|
|
51431
52633
|
};
|
|
51432
|
-
if (updates.tokenBudget) {
|
|
51433
|
-
this.tokenBudgetManager.updateConfig(this.config.tokenBudget);
|
|
51434
|
-
}
|
|
51435
52634
|
this.injector.updateConfig(updates);
|
|
51436
52635
|
if (updates.todoIntegration && this.todoProvider) {
|
|
51437
52636
|
const current = this.todoProvider.getConfig();
|