@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/mcp/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as path4 from 'path';
|
|
|
3
3
|
import path4__default, { dirname, join, extname as extname$1, basename, resolve, relative, isAbsolute, sep, parse, delimiter } from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { mkdir, appendFile, readFile, readdir, writeFile, rename, unlink, copyFile, access, stat, realpath } from 'fs/promises';
|
|
6
|
+
import * as fs3 from 'fs';
|
|
6
7
|
import { existsSync, readFileSync, promises, mkdirSync, createWriteStream, writeFileSync, unlinkSync, constants } from 'fs';
|
|
7
8
|
import Database2 from 'better-sqlite3';
|
|
8
9
|
import { glob } from 'glob';
|
|
@@ -23,9 +24,7 @@ import { randomUUID } from 'crypto';
|
|
|
23
24
|
import yaml from 'yaml';
|
|
24
25
|
|
|
25
26
|
var __defProp = Object.defineProperty;
|
|
26
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
27
27
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
28
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
29
28
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
30
29
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
31
30
|
}) : x)(function(x) {
|
|
@@ -39,15 +38,6 @@ var __export = (target, all) => {
|
|
|
39
38
|
for (var name in all)
|
|
40
39
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
41
40
|
};
|
|
42
|
-
var __copyProps = (to, from, except, desc) => {
|
|
43
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
44
|
-
for (let key of __getOwnPropNames(from))
|
|
45
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
46
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
47
|
-
}
|
|
48
|
-
return to;
|
|
49
|
-
};
|
|
50
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
51
41
|
var init_esm_shims = __esm({
|
|
52
42
|
"node_modules/tsup/assets/esm_shims.js"() {
|
|
53
43
|
}
|
|
@@ -328,16 +318,16 @@ var init_errors = __esm({
|
|
|
328
318
|
constructor(message, code = "E1001" /* CONFIG_INVALID */, suggestions = [], context) {
|
|
329
319
|
super(message, code, suggestions, context);
|
|
330
320
|
}
|
|
331
|
-
static notFound(
|
|
321
|
+
static notFound(path6) {
|
|
332
322
|
return new _ConfigError(
|
|
333
|
-
`Configuration file not found: ${
|
|
323
|
+
`Configuration file not found: ${path6}`,
|
|
334
324
|
"E1000" /* CONFIG_NOT_FOUND */,
|
|
335
325
|
[
|
|
336
326
|
'Run "automatosx setup" to create a new configuration',
|
|
337
327
|
"Specify a custom config path with --config option",
|
|
338
328
|
"Check that you are in a valid AutomatosX project directory"
|
|
339
329
|
],
|
|
340
|
-
{ path:
|
|
330
|
+
{ path: path6 }
|
|
341
331
|
);
|
|
342
332
|
}
|
|
343
333
|
static invalid(reason, context) {
|
|
@@ -352,7 +342,7 @@ var init_errors = __esm({
|
|
|
352
342
|
context
|
|
353
343
|
);
|
|
354
344
|
}
|
|
355
|
-
static parseError(error,
|
|
345
|
+
static parseError(error, path6) {
|
|
356
346
|
return new _ConfigError(
|
|
357
347
|
`Failed to parse configuration: ${error.message}`,
|
|
358
348
|
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
@@ -361,7 +351,7 @@ var init_errors = __esm({
|
|
|
361
351
|
"Use a JSON validator to find syntax errors",
|
|
362
352
|
'Reset to default with "automatosx setup --force"'
|
|
363
353
|
],
|
|
364
|
-
{ path:
|
|
354
|
+
{ path: path6, originalError: error.message }
|
|
365
355
|
);
|
|
366
356
|
}
|
|
367
357
|
};
|
|
@@ -1174,11 +1164,11 @@ var init_workspace_indexer = __esm({
|
|
|
1174
1164
|
/**
|
|
1175
1165
|
* Detect file type and language
|
|
1176
1166
|
*/
|
|
1177
|
-
detectTypeAndLanguage(
|
|
1178
|
-
if (
|
|
1167
|
+
detectTypeAndLanguage(path6, ext) {
|
|
1168
|
+
if (path6.match(/package\.json|tsconfig\.json|\.config\.(js|ts|json|yaml|yml)|\.eslintrc|\.prettierrc/)) {
|
|
1179
1169
|
return { type: "config", language: "json" };
|
|
1180
1170
|
}
|
|
1181
|
-
if (
|
|
1171
|
+
if (path6.match(/\.(test|spec)\.(ts|js|tsx|jsx)$/) || path6.includes("__tests__")) {
|
|
1182
1172
|
const lang = LANGUAGE_MAP[ext];
|
|
1183
1173
|
return { type: "test", language: lang };
|
|
1184
1174
|
}
|
|
@@ -1195,15 +1185,15 @@ var init_workspace_indexer = __esm({
|
|
|
1195
1185
|
*
|
|
1196
1186
|
* Higher score = more likely to be relevant
|
|
1197
1187
|
*/
|
|
1198
|
-
calculateImportance(
|
|
1188
|
+
calculateImportance(path6, type, size) {
|
|
1199
1189
|
let score = 0.5;
|
|
1200
|
-
if (
|
|
1201
|
-
if (
|
|
1202
|
-
if (
|
|
1190
|
+
if (path6.match(/^(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 1;
|
|
1191
|
+
if (path6.match(/^src\/(index|main|app)\.(ts|js|tsx|jsx)$/)) score = 0.95;
|
|
1192
|
+
if (path6.match(/^src\/cli\/(index|main)\.(ts|js)$/)) score = 0.9;
|
|
1203
1193
|
if (type === "config") score = 0.85;
|
|
1204
|
-
if (
|
|
1205
|
-
if (
|
|
1206
|
-
if (
|
|
1194
|
+
if (path6.includes("/core/")) score += 0.15;
|
|
1195
|
+
if (path6.includes("/types/")) score += 0.1;
|
|
1196
|
+
if (path6.includes("/utils/")) score += 0.05;
|
|
1207
1197
|
if (type === "test") score = Math.max(0.3, score - 0.3);
|
|
1208
1198
|
if (type === "doc") score = Math.max(0.4, score - 0.2);
|
|
1209
1199
|
if (size > 1e4) score += 0.05;
|
|
@@ -1679,10 +1669,10 @@ function findOnPathUnix(cmdBase) {
|
|
|
1679
1669
|
try {
|
|
1680
1670
|
const which = spawnSync("which", [cmdBase], { timeout: 3e3 });
|
|
1681
1671
|
if (which.status === 0) {
|
|
1682
|
-
const
|
|
1683
|
-
if (
|
|
1684
|
-
logger.debug("Found via which", { cmdBase, path:
|
|
1685
|
-
return { found: true, path:
|
|
1672
|
+
const path6 = which.stdout.toString().trim();
|
|
1673
|
+
if (path6) {
|
|
1674
|
+
logger.debug("Found via which", { cmdBase, path: path6 });
|
|
1675
|
+
return { found: true, path: path6 };
|
|
1686
1676
|
}
|
|
1687
1677
|
}
|
|
1688
1678
|
} catch (error) {
|
|
@@ -1723,8 +1713,8 @@ var init_provider_schemas = __esm({
|
|
|
1723
1713
|
completion: z.number().int().nonnegative(),
|
|
1724
1714
|
total: z.number().int().nonnegative()
|
|
1725
1715
|
}).refine(
|
|
1726
|
-
(data) => data.total
|
|
1727
|
-
"Total tokens must equal prompt + completion"
|
|
1716
|
+
(data) => data.total >= data.prompt + data.completion,
|
|
1717
|
+
"Total tokens must be greater than or equal to prompt + completion"
|
|
1728
1718
|
).describe("Token usage information");
|
|
1729
1719
|
finishReasonSchema = z.enum([
|
|
1730
1720
|
"stop",
|
|
@@ -1981,15 +1971,6 @@ var init_streaming_progress_parser = __esm({
|
|
|
1981
1971
|
});
|
|
1982
1972
|
|
|
1983
1973
|
// src/providers/error-patterns.ts
|
|
1984
|
-
var error_patterns_exports = {};
|
|
1985
|
-
__export(error_patterns_exports, {
|
|
1986
|
-
GENERIC_ERROR_PATTERNS: () => GENERIC_ERROR_PATTERNS,
|
|
1987
|
-
PROVIDER_ERROR_PATTERNS: () => PROVIDER_ERROR_PATTERNS,
|
|
1988
|
-
getProviderErrorPatterns: () => getProviderErrorPatterns,
|
|
1989
|
-
isLimitError: () => isLimitError,
|
|
1990
|
-
isQuotaError: () => isQuotaError,
|
|
1991
|
-
isRateLimitError: () => isRateLimitError
|
|
1992
|
-
});
|
|
1993
1974
|
function isQuotaError(error, providerName) {
|
|
1994
1975
|
const patterns = PROVIDER_ERROR_PATTERNS[providerName] || GENERIC_ERROR_PATTERNS;
|
|
1995
1976
|
const message = (error?.message || "").toLowerCase();
|
|
@@ -2031,9 +2012,6 @@ function isRateLimitError(error, providerName) {
|
|
|
2031
2012
|
function isLimitError(error, providerName) {
|
|
2032
2013
|
return isQuotaError(error, providerName) || isRateLimitError(error, providerName);
|
|
2033
2014
|
}
|
|
2034
|
-
function getProviderErrorPatterns(providerName) {
|
|
2035
|
-
return PROVIDER_ERROR_PATTERNS[providerName] || GENERIC_ERROR_PATTERNS;
|
|
2036
|
-
}
|
|
2037
2015
|
var PROVIDER_ERROR_PATTERNS, GENERIC_ERROR_PATTERNS;
|
|
2038
2016
|
var init_error_patterns = __esm({
|
|
2039
2017
|
"src/providers/error-patterns.ts"() {
|
|
@@ -2222,6 +2200,62 @@ var init_error_patterns = __esm({
|
|
|
2222
2200
|
"quota_exceeded",
|
|
2223
2201
|
"billing_hard_limit_reached"
|
|
2224
2202
|
]
|
|
2203
|
+
},
|
|
2204
|
+
/**
|
|
2205
|
+
* ax-cli Provider Error Patterns (v9.2.0)
|
|
2206
|
+
*
|
|
2207
|
+
* ax-cli is a multi-model provider supporting GLM, xAI, OpenAI, Anthropic, Ollama, etc.
|
|
2208
|
+
* BUG FIX: Added missing patterns - previously fell back to generic patterns which
|
|
2209
|
+
* may miss provider-specific errors from the underlying model providers.
|
|
2210
|
+
*
|
|
2211
|
+
* Combines patterns from all supported backends since ax-cli proxies to them.
|
|
2212
|
+
*/
|
|
2213
|
+
"ax-cli": {
|
|
2214
|
+
quota: [
|
|
2215
|
+
// GLM patterns
|
|
2216
|
+
"quota exceeded",
|
|
2217
|
+
"quota limit reached",
|
|
2218
|
+
"insufficient quota",
|
|
2219
|
+
// OpenAI patterns (via ax-cli)
|
|
2220
|
+
"insufficient_quota",
|
|
2221
|
+
"quota_exceeded",
|
|
2222
|
+
"billing hard limit reached",
|
|
2223
|
+
"usage limit exceeded",
|
|
2224
|
+
"monthly quota exceeded",
|
|
2225
|
+
"credit limit reached",
|
|
2226
|
+
// Anthropic patterns (via ax-cli)
|
|
2227
|
+
"credit limit reached",
|
|
2228
|
+
// Generic patterns
|
|
2229
|
+
"daily quota exceeded",
|
|
2230
|
+
"api quota exceeded"
|
|
2231
|
+
],
|
|
2232
|
+
rateLimit: [
|
|
2233
|
+
// Common patterns
|
|
2234
|
+
"rate_limit_exceeded",
|
|
2235
|
+
"rate limit exceeded",
|
|
2236
|
+
"too_many_requests",
|
|
2237
|
+
"too many requests",
|
|
2238
|
+
"requests per minute exceeded",
|
|
2239
|
+
"tokens per minute exceeded",
|
|
2240
|
+
"rate limit reached",
|
|
2241
|
+
// Anthropic patterns (via ax-cli)
|
|
2242
|
+
"rate_limit_error",
|
|
2243
|
+
"overloaded_error",
|
|
2244
|
+
"overloaded",
|
|
2245
|
+
// xAI/Grok patterns
|
|
2246
|
+
"throttled",
|
|
2247
|
+
"request throttled"
|
|
2248
|
+
],
|
|
2249
|
+
statusCodes: [429, 529],
|
|
2250
|
+
errorCodes: [
|
|
2251
|
+
"insufficient_quota",
|
|
2252
|
+
"rate_limit_exceeded",
|
|
2253
|
+
"quota_exceeded",
|
|
2254
|
+
"rate_limit_error",
|
|
2255
|
+
"overloaded_error",
|
|
2256
|
+
"RATE_LIMIT_EXCEEDED",
|
|
2257
|
+
"QUOTA_EXCEEDED"
|
|
2258
|
+
]
|
|
2225
2259
|
}
|
|
2226
2260
|
};
|
|
2227
2261
|
GENERIC_ERROR_PATTERNS = {
|
|
@@ -2244,6 +2278,105 @@ var init_error_patterns = __esm({
|
|
|
2244
2278
|
};
|
|
2245
2279
|
}
|
|
2246
2280
|
});
|
|
2281
|
+
|
|
2282
|
+
// src/providers/retry-errors.ts
|
|
2283
|
+
function containsErrorPattern(message, patterns) {
|
|
2284
|
+
const lowerMessage = message.toLowerCase();
|
|
2285
|
+
return patterns.some((pattern) => lowerMessage.includes(pattern.toLowerCase()));
|
|
2286
|
+
}
|
|
2287
|
+
function getRetryableErrors(provider) {
|
|
2288
|
+
const baseErrors = [
|
|
2289
|
+
...COMMON_NETWORK_ERRORS,
|
|
2290
|
+
...COMMON_RATE_LIMIT_ERRORS,
|
|
2291
|
+
...COMMON_SERVER_ERRORS
|
|
2292
|
+
];
|
|
2293
|
+
switch (provider) {
|
|
2294
|
+
case "claude":
|
|
2295
|
+
return [...baseErrors, ...CLAUDE_RETRYABLE_ERRORS];
|
|
2296
|
+
case "gemini":
|
|
2297
|
+
return [...baseErrors, ...GEMINI_RETRYABLE_ERRORS];
|
|
2298
|
+
case "openai":
|
|
2299
|
+
return [...baseErrors, ...OPENAI_RETRYABLE_ERRORS];
|
|
2300
|
+
case "ax-cli":
|
|
2301
|
+
return [...baseErrors, ...AX_CLI_RETRYABLE_ERRORS];
|
|
2302
|
+
case "codex":
|
|
2303
|
+
return [...baseErrors, ...CODEX_RETRYABLE_ERRORS];
|
|
2304
|
+
case "base":
|
|
2305
|
+
default:
|
|
2306
|
+
return baseErrors;
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
function shouldRetryError(error, provider) {
|
|
2310
|
+
const message = error.message;
|
|
2311
|
+
if (containsErrorPattern(message, NON_RETRYABLE_ERRORS)) {
|
|
2312
|
+
return false;
|
|
2313
|
+
}
|
|
2314
|
+
const retryableErrors = getRetryableErrors(provider);
|
|
2315
|
+
return containsErrorPattern(message, retryableErrors);
|
|
2316
|
+
}
|
|
2317
|
+
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;
|
|
2318
|
+
var init_retry_errors = __esm({
|
|
2319
|
+
"src/providers/retry-errors.ts"() {
|
|
2320
|
+
init_esm_shims();
|
|
2321
|
+
COMMON_NETWORK_ERRORS = [
|
|
2322
|
+
"ECONNRESET",
|
|
2323
|
+
"ETIMEDOUT",
|
|
2324
|
+
"ENOTFOUND",
|
|
2325
|
+
"ECONNREFUSED",
|
|
2326
|
+
"connection_error",
|
|
2327
|
+
"network connection error",
|
|
2328
|
+
"timeout"
|
|
2329
|
+
];
|
|
2330
|
+
COMMON_RATE_LIMIT_ERRORS = [
|
|
2331
|
+
"rate_limit",
|
|
2332
|
+
"rate_limit_error",
|
|
2333
|
+
"too many requests"
|
|
2334
|
+
];
|
|
2335
|
+
COMMON_SERVER_ERRORS = [
|
|
2336
|
+
"internal_server_error",
|
|
2337
|
+
"server_error",
|
|
2338
|
+
"service_unavailable",
|
|
2339
|
+
"unavailable"
|
|
2340
|
+
];
|
|
2341
|
+
CLAUDE_RETRYABLE_ERRORS = [
|
|
2342
|
+
"overloaded_error",
|
|
2343
|
+
"internal_server_error"
|
|
2344
|
+
];
|
|
2345
|
+
GEMINI_RETRYABLE_ERRORS = [
|
|
2346
|
+
"resource_exhausted",
|
|
2347
|
+
"deadline_exceeded",
|
|
2348
|
+
"internal"
|
|
2349
|
+
];
|
|
2350
|
+
OPENAI_RETRYABLE_ERRORS = [
|
|
2351
|
+
"internal_error"
|
|
2352
|
+
];
|
|
2353
|
+
AX_CLI_RETRYABLE_ERRORS = [
|
|
2354
|
+
// Inherited from Anthropic (via ax-cli)
|
|
2355
|
+
"overloaded_error",
|
|
2356
|
+
// Inherited from OpenAI (via ax-cli)
|
|
2357
|
+
"internal_error",
|
|
2358
|
+
// ax-cli specific
|
|
2359
|
+
"agent_error",
|
|
2360
|
+
"tool_execution_error",
|
|
2361
|
+
// xAI/Grok specific
|
|
2362
|
+
"service_overloaded"
|
|
2363
|
+
];
|
|
2364
|
+
CODEX_RETRYABLE_ERRORS = [
|
|
2365
|
+
...OPENAI_RETRYABLE_ERRORS,
|
|
2366
|
+
"sandbox_error"
|
|
2367
|
+
// Codex-specific sandbox issues are often transient
|
|
2368
|
+
];
|
|
2369
|
+
NON_RETRYABLE_ERRORS = [
|
|
2370
|
+
"authentication",
|
|
2371
|
+
"unauthorized",
|
|
2372
|
+
"api key",
|
|
2373
|
+
"not found",
|
|
2374
|
+
"permission denied",
|
|
2375
|
+
"invalid_api_key",
|
|
2376
|
+
"invalid_request"
|
|
2377
|
+
];
|
|
2378
|
+
}
|
|
2379
|
+
});
|
|
2247
2380
|
var BaseProvider;
|
|
2248
2381
|
var init_base_provider = __esm({
|
|
2249
2382
|
"src/providers/base-provider.ts"() {
|
|
@@ -2254,6 +2387,8 @@ var init_base_provider = __esm({
|
|
|
2254
2387
|
init_provider_schemas();
|
|
2255
2388
|
init_streaming_progress_parser();
|
|
2256
2389
|
init_verbosity_manager();
|
|
2390
|
+
init_error_patterns();
|
|
2391
|
+
init_retry_errors();
|
|
2257
2392
|
BaseProvider = class _BaseProvider {
|
|
2258
2393
|
/**
|
|
2259
2394
|
* Whitelist of allowed provider names for security
|
|
@@ -2286,6 +2421,10 @@ var init_base_provider = __esm({
|
|
|
2286
2421
|
NO_UPDATE_NOTIFIER: "1",
|
|
2287
2422
|
DEBIAN_FRONTEND: "noninteractive"
|
|
2288
2423
|
};
|
|
2424
|
+
/** Default CLI execution timeout in milliseconds */
|
|
2425
|
+
static DEFAULT_TIMEOUT_MS = 12e4;
|
|
2426
|
+
/** Time to wait after SIGTERM before escalating to SIGKILL */
|
|
2427
|
+
static SIGKILL_ESCALATION_MS = 5e3;
|
|
2289
2428
|
config;
|
|
2290
2429
|
logger = logger;
|
|
2291
2430
|
health;
|
|
@@ -2303,6 +2442,8 @@ var init_base_provider = __esm({
|
|
|
2303
2442
|
latencyMs: 0,
|
|
2304
2443
|
errorRate: 0,
|
|
2305
2444
|
consecutiveFailures: 0,
|
|
2445
|
+
consecutiveSuccesses: 0,
|
|
2446
|
+
// BUG FIX: Track consecutive successes
|
|
2306
2447
|
lastCheck: Date.now()
|
|
2307
2448
|
};
|
|
2308
2449
|
}
|
|
@@ -2343,7 +2484,7 @@ var init_base_provider = __esm({
|
|
|
2343
2484
|
useStdin,
|
|
2344
2485
|
streaming: process.env.AUTOMATOSX_SHOW_PROVIDER_OUTPUT === "true"
|
|
2345
2486
|
});
|
|
2346
|
-
const result = useStdin ? await this.executeWithStdin(cliCommand,
|
|
2487
|
+
const result = useStdin ? await this.executeWithStdin(cliCommand, cliArgs, prompt) : await this.executeWithSpawn(fullCommand, cliCommand);
|
|
2347
2488
|
if (!result.stdout) {
|
|
2348
2489
|
throw new Error(`${cliCommand} CLI returned empty output. stderr: ${result.stderr || "none"}`);
|
|
2349
2490
|
}
|
|
@@ -2365,8 +2506,8 @@ var init_base_provider = __esm({
|
|
|
2365
2506
|
/**
|
|
2366
2507
|
* Execute command using spawn() with streaming support (v8.4.18)
|
|
2367
2508
|
*
|
|
2368
|
-
*
|
|
2369
|
-
*
|
|
2509
|
+
* Supports real-time line-by-line output streaming with proper timeout
|
|
2510
|
+
* and cleanup behaviors (SIGTERM → SIGKILL escalation pattern).
|
|
2370
2511
|
*
|
|
2371
2512
|
* @param command - Full command string to execute
|
|
2372
2513
|
* @param cliCommand - CLI command name (for logging)
|
|
@@ -2377,7 +2518,7 @@ var init_base_provider = __esm({
|
|
|
2377
2518
|
const child = spawn(command, [], {
|
|
2378
2519
|
shell: true,
|
|
2379
2520
|
// Auto-detects: cmd.exe on Windows, /bin/sh on Unix
|
|
2380
|
-
timeout: this.config.timeout ||
|
|
2521
|
+
timeout: this.config.timeout || _BaseProvider.DEFAULT_TIMEOUT_MS,
|
|
2381
2522
|
env: { ...process.env, ..._BaseProvider.NON_INTERACTIVE_ENV }
|
|
2382
2523
|
});
|
|
2383
2524
|
let stdout = "";
|
|
@@ -2488,7 +2629,7 @@ var init_base_provider = __esm({
|
|
|
2488
2629
|
});
|
|
2489
2630
|
reject(new Error(`Failed to spawn ${cliCommand} CLI: ${error.message}`));
|
|
2490
2631
|
});
|
|
2491
|
-
const timeout = this.config.timeout ||
|
|
2632
|
+
const timeout = this.config.timeout || _BaseProvider.DEFAULT_TIMEOUT_MS;
|
|
2492
2633
|
timeoutId = setTimeout(() => {
|
|
2493
2634
|
if (child.pid && !child.killed) {
|
|
2494
2635
|
logger.warn("Killing child process due to timeout", {
|
|
@@ -2502,7 +2643,7 @@ var init_base_provider = __esm({
|
|
|
2502
2643
|
logger.warn("Force killing child process", { pid: child.pid });
|
|
2503
2644
|
child.kill("SIGKILL");
|
|
2504
2645
|
}
|
|
2505
|
-
},
|
|
2646
|
+
}, _BaseProvider.SIGKILL_ESCALATION_MS);
|
|
2506
2647
|
}
|
|
2507
2648
|
}, timeout);
|
|
2508
2649
|
});
|
|
@@ -2520,14 +2661,13 @@ var init_base_provider = __esm({
|
|
|
2520
2661
|
*/
|
|
2521
2662
|
async executeWithStdin(cliCommand, cliArgs, prompt) {
|
|
2522
2663
|
return new Promise((resolve5, reject) => {
|
|
2523
|
-
const commandArgs = cliArgs ? cliArgs.split(" ").filter(Boolean) : [];
|
|
2524
2664
|
logger.debug(`Executing ${cliCommand} CLI with stdin`, {
|
|
2525
2665
|
command: cliCommand,
|
|
2526
|
-
args:
|
|
2666
|
+
args: cliArgs,
|
|
2527
2667
|
promptLength: prompt.length
|
|
2528
2668
|
});
|
|
2529
|
-
const child = spawn(cliCommand,
|
|
2530
|
-
timeout: this.config.timeout ||
|
|
2669
|
+
const child = spawn(cliCommand, cliArgs, {
|
|
2670
|
+
timeout: this.config.timeout || _BaseProvider.DEFAULT_TIMEOUT_MS,
|
|
2531
2671
|
env: { ...process.env, ..._BaseProvider.NON_INTERACTIVE_ENV }
|
|
2532
2672
|
});
|
|
2533
2673
|
let stdout = "";
|
|
@@ -2550,11 +2690,20 @@ var init_base_provider = __esm({
|
|
|
2550
2690
|
child.stdin.write(prompt);
|
|
2551
2691
|
child.stdin.end();
|
|
2552
2692
|
} catch (error) {
|
|
2693
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2553
2694
|
logger.error("Failed to write to stdin", {
|
|
2554
2695
|
command: cliCommand,
|
|
2555
|
-
error:
|
|
2696
|
+
error: errorMessage
|
|
2556
2697
|
});
|
|
2698
|
+
child.kill("SIGTERM");
|
|
2699
|
+
reject(new Error(`Failed to write prompt to ${cliCommand} stdin: ${errorMessage}`));
|
|
2700
|
+
return;
|
|
2557
2701
|
}
|
|
2702
|
+
} else {
|
|
2703
|
+
logger.error("stdin not available for child process", { command: cliCommand });
|
|
2704
|
+
child.kill("SIGTERM");
|
|
2705
|
+
reject(new Error(`${cliCommand} stdin not available - cannot send prompt`));
|
|
2706
|
+
return;
|
|
2558
2707
|
}
|
|
2559
2708
|
if (child.stdout) {
|
|
2560
2709
|
readlineInterface = readline.createInterface({
|
|
@@ -2663,7 +2812,7 @@ var init_base_provider = __esm({
|
|
|
2663
2812
|
});
|
|
2664
2813
|
reject(new Error(`Failed to spawn ${cliCommand} CLI: ${error.message}`));
|
|
2665
2814
|
});
|
|
2666
|
-
const timeout = this.config.timeout ||
|
|
2815
|
+
const timeout = this.config.timeout || _BaseProvider.DEFAULT_TIMEOUT_MS;
|
|
2667
2816
|
timeoutId = setTimeout(() => {
|
|
2668
2817
|
if (child.pid && !child.killed) {
|
|
2669
2818
|
logger.warn("Killing child process due to timeout", {
|
|
@@ -2677,7 +2826,7 @@ var init_base_provider = __esm({
|
|
|
2677
2826
|
logger.warn("Force killing child process", { pid: child.pid });
|
|
2678
2827
|
child.kill("SIGKILL");
|
|
2679
2828
|
}
|
|
2680
|
-
},
|
|
2829
|
+
}, _BaseProvider.SIGKILL_ESCALATION_MS);
|
|
2681
2830
|
}
|
|
2682
2831
|
}, timeout);
|
|
2683
2832
|
});
|
|
@@ -2743,9 +2892,7 @@ var init_base_provider = __esm({
|
|
|
2743
2892
|
${request.prompt}`;
|
|
2744
2893
|
}
|
|
2745
2894
|
if (process.env.AUTOMATOSX_DEBUG_PROMPT === "true") {
|
|
2746
|
-
const
|
|
2747
|
-
const path5 = await import('path');
|
|
2748
|
-
const debugPath = path5.join(process.cwd(), "automatosx/tmp/debug-prompt.txt");
|
|
2895
|
+
const debugPath = path4.join(process.cwd(), "automatosx/tmp/debug-prompt.txt");
|
|
2749
2896
|
fs3.writeFileSync(debugPath, `=== FULL PROMPT SENT TO ${this.getCLICommand()} ===
|
|
2750
2897
|
|
|
2751
2898
|
${fullPrompt}
|
|
@@ -2757,6 +2904,7 @@ ${fullPrompt}
|
|
|
2757
2904
|
const result = await this.executeCLI(fullPrompt);
|
|
2758
2905
|
const latencyMs = Date.now() - startTime;
|
|
2759
2906
|
this.health.consecutiveFailures = 0;
|
|
2907
|
+
this.health.consecutiveSuccesses++;
|
|
2760
2908
|
this.health.available = true;
|
|
2761
2909
|
this.health.errorRate = 0;
|
|
2762
2910
|
this.health.latencyMs = latencyMs;
|
|
@@ -2789,6 +2937,7 @@ ${fullPrompt}
|
|
|
2789
2937
|
return response;
|
|
2790
2938
|
} catch (error) {
|
|
2791
2939
|
this.health.consecutiveFailures++;
|
|
2940
|
+
this.health.consecutiveSuccesses = 0;
|
|
2792
2941
|
this.health.available = false;
|
|
2793
2942
|
this.health.errorRate = 1;
|
|
2794
2943
|
this.health.latencyMs = Date.now() - startTime;
|
|
@@ -2802,19 +2951,23 @@ ${fullPrompt}
|
|
|
2802
2951
|
async isAvailable() {
|
|
2803
2952
|
try {
|
|
2804
2953
|
const available = await this.checkCLIAvailable();
|
|
2805
|
-
this.health =
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
}
|
|
2954
|
+
this.health.available = available;
|
|
2955
|
+
this.health.errorRate = available ? 0 : 1;
|
|
2956
|
+
this.health.lastCheck = Date.now();
|
|
2957
|
+
if (available) {
|
|
2958
|
+
this.health.consecutiveFailures = 0;
|
|
2959
|
+
this.health.consecutiveSuccesses++;
|
|
2960
|
+
} else {
|
|
2961
|
+
this.health.consecutiveFailures++;
|
|
2962
|
+
this.health.consecutiveSuccesses = 0;
|
|
2963
|
+
}
|
|
2812
2964
|
return available;
|
|
2813
2965
|
} catch (error) {
|
|
2814
2966
|
this.health.available = false;
|
|
2815
2967
|
this.health.errorRate = 1;
|
|
2816
2968
|
this.health.lastCheck = Date.now();
|
|
2817
|
-
this.health.consecutiveFailures
|
|
2969
|
+
this.health.consecutiveFailures++;
|
|
2970
|
+
this.health.consecutiveSuccesses = 0;
|
|
2818
2971
|
return false;
|
|
2819
2972
|
}
|
|
2820
2973
|
}
|
|
@@ -2869,26 +3022,16 @@ ${fullPrompt}
|
|
|
2869
3022
|
* @returns true if this is a rate limit or quota error
|
|
2870
3023
|
*/
|
|
2871
3024
|
detectRateLimitError(error) {
|
|
2872
|
-
const
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
if (isLimited) {
|
|
2876
|
-
this.logger.debug("Rate limit error detected", {
|
|
2877
|
-
provider: this.config.name,
|
|
2878
|
-
message: error?.message,
|
|
2879
|
-
code: error?.code,
|
|
2880
|
-
status: error?.status || error?.statusCode
|
|
2881
|
-
});
|
|
2882
|
-
}
|
|
2883
|
-
return isLimited;
|
|
2884
|
-
} catch (detectionError) {
|
|
2885
|
-
this.logger.warn("Rate limit detection failed, using fallback", {
|
|
3025
|
+
const isLimited = isLimitError(error, this.config.name);
|
|
3026
|
+
if (isLimited) {
|
|
3027
|
+
this.logger.debug("Rate limit error detected", {
|
|
2886
3028
|
provider: this.config.name,
|
|
2887
|
-
|
|
3029
|
+
message: error?.message,
|
|
3030
|
+
code: error?.code,
|
|
3031
|
+
status: error?.status || error?.statusCode
|
|
2888
3032
|
});
|
|
2889
|
-
const message = (error?.message || "").toLowerCase();
|
|
2890
|
-
return message.includes("rate limit") || message.includes("quota") || message.includes("resource_exhausted") || message.includes("too many requests");
|
|
2891
3033
|
}
|
|
3034
|
+
return isLimited;
|
|
2892
3035
|
}
|
|
2893
3036
|
/**
|
|
2894
3037
|
* Escape shell command arguments to prevent injection
|
|
@@ -2981,8 +3124,20 @@ ${fullPrompt}
|
|
|
2981
3124
|
};
|
|
2982
3125
|
}
|
|
2983
3126
|
shouldRetry(error) {
|
|
2984
|
-
const
|
|
2985
|
-
|
|
3127
|
+
const providerName = this.config.name.toLowerCase();
|
|
3128
|
+
let retryableProvider = "base";
|
|
3129
|
+
if (providerName === "claude" || providerName === "claude-code") {
|
|
3130
|
+
retryableProvider = "claude";
|
|
3131
|
+
} else if (providerName === "gemini" || providerName === "gemini-cli") {
|
|
3132
|
+
retryableProvider = "gemini";
|
|
3133
|
+
} else if (providerName === "openai") {
|
|
3134
|
+
retryableProvider = "openai";
|
|
3135
|
+
} else if (providerName === "codex") {
|
|
3136
|
+
retryableProvider = "codex";
|
|
3137
|
+
} else if (providerName === "ax-cli" || providerName === "glm") {
|
|
3138
|
+
retryableProvider = "ax-cli";
|
|
3139
|
+
}
|
|
3140
|
+
return shouldRetryError(error, retryableProvider);
|
|
2986
3141
|
}
|
|
2987
3142
|
getRetryDelay(attempt) {
|
|
2988
3143
|
return Math.min(1e3 * Math.pow(2, attempt - 1), 3e4);
|
|
@@ -3006,7 +3161,9 @@ ${fullPrompt}
|
|
|
3006
3161
|
},
|
|
3007
3162
|
health: {
|
|
3008
3163
|
consecutiveFailures: this.health.consecutiveFailures,
|
|
3009
|
-
|
|
3164
|
+
// BUG FIX: Use actual tracked value instead of incorrect derivation
|
|
3165
|
+
// Previously: `consecutiveFailures === 0 ? 1 : 0` which was wrong
|
|
3166
|
+
consecutiveSuccesses: this.health.consecutiveSuccesses,
|
|
3010
3167
|
lastCheckTime: this.health.lastCheck,
|
|
3011
3168
|
lastCheckDuration: 0,
|
|
3012
3169
|
uptime: this.health.lastCheck > 0 ? Date.now() - this.health.lastCheck : 0
|
|
@@ -3706,7 +3863,20 @@ var init_sdk_adapter = __esm({
|
|
|
3706
3863
|
}
|
|
3707
3864
|
}
|
|
3708
3865
|
const tokenCount = result.usage ? (result.usage.input_tokens || 0) + (result.usage.output_tokens || 0) : void 0;
|
|
3709
|
-
if (!this.options.reuseThreads) {
|
|
3866
|
+
if (!this.options.reuseThreads && this.activeThread) {
|
|
3867
|
+
try {
|
|
3868
|
+
const threadWithDispose = this.activeThread;
|
|
3869
|
+
if (typeof threadWithDispose.dispose === "function") {
|
|
3870
|
+
const disposeResult = threadWithDispose.dispose();
|
|
3871
|
+
if (disposeResult instanceof Promise) {
|
|
3872
|
+
await disposeResult;
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3875
|
+
} catch (disposeError) {
|
|
3876
|
+
logger.warn("Error disposing thread", {
|
|
3877
|
+
error: disposeError instanceof Error ? disposeError.message : String(disposeError)
|
|
3878
|
+
});
|
|
3879
|
+
}
|
|
3710
3880
|
this.activeThread = null;
|
|
3711
3881
|
}
|
|
3712
3882
|
return {
|
|
@@ -3885,12 +4055,38 @@ var init_hybrid_adapter = __esm({
|
|
|
3885
4055
|
}
|
|
3886
4056
|
this.activeMode = "cli";
|
|
3887
4057
|
}
|
|
4058
|
+
/**
|
|
4059
|
+
* Cleanup resources
|
|
4060
|
+
*
|
|
4061
|
+
* BUG FIX: Use Promise.allSettled to ensure cleanup continues even if one
|
|
4062
|
+
* adapter fails, and properly log any cleanup errors. Previously, a failure
|
|
4063
|
+
* in SDK destroy would prevent CLI cleanup from running.
|
|
4064
|
+
*/
|
|
3888
4065
|
async destroy() {
|
|
3889
|
-
|
|
3890
|
-
if (this.
|
|
4066
|
+
const cleanupPromises = [];
|
|
4067
|
+
if (this.sdkAdapter) {
|
|
4068
|
+
cleanupPromises.push(
|
|
4069
|
+
this.sdkAdapter.destroy().catch((err) => {
|
|
4070
|
+
logger.warn("Error destroying SDK adapter", {
|
|
4071
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4072
|
+
});
|
|
4073
|
+
})
|
|
4074
|
+
);
|
|
4075
|
+
}
|
|
4076
|
+
if (this.cliAdapter) {
|
|
4077
|
+
cleanupPromises.push(
|
|
4078
|
+
this.cliAdapter.cleanup().catch((err) => {
|
|
4079
|
+
logger.warn("Error cleaning up CLI adapter", {
|
|
4080
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4081
|
+
});
|
|
4082
|
+
})
|
|
4083
|
+
);
|
|
4084
|
+
}
|
|
4085
|
+
await Promise.allSettled(cleanupPromises);
|
|
3891
4086
|
this.sdkAdapter = null;
|
|
3892
4087
|
this.cliAdapter = null;
|
|
3893
4088
|
this.activeMode = null;
|
|
4089
|
+
logger.debug("HybridCodexAdapter destroyed");
|
|
3894
4090
|
}
|
|
3895
4091
|
};
|
|
3896
4092
|
}
|
|
@@ -3947,7 +4143,7 @@ var init_openai_provider = __esm({
|
|
|
3947
4143
|
error: error instanceof Error ? error.message : String(error),
|
|
3948
4144
|
mode: this.hybridAdapter?.getActiveMode() || "unknown"
|
|
3949
4145
|
});
|
|
3950
|
-
throw error;
|
|
4146
|
+
throw this.handleError(error);
|
|
3951
4147
|
}
|
|
3952
4148
|
}
|
|
3953
4149
|
initializeHybridAdapter() {
|
|
@@ -4578,11 +4774,11 @@ var VALIDATION_LIMITS = {
|
|
|
4578
4774
|
MAX_PORT: 65535
|
|
4579
4775
|
// Maximum port number
|
|
4580
4776
|
};
|
|
4581
|
-
function isValidRelativePath(
|
|
4582
|
-
if (!
|
|
4777
|
+
function isValidRelativePath(path6) {
|
|
4778
|
+
if (!path6 || typeof path6 !== "string") {
|
|
4583
4779
|
return false;
|
|
4584
4780
|
}
|
|
4585
|
-
const normalizedPath =
|
|
4781
|
+
const normalizedPath = path6.replace(/\\/g, "/");
|
|
4586
4782
|
if (normalizedPath.startsWith("/")) {
|
|
4587
4783
|
return false;
|
|
4588
4784
|
}
|
|
@@ -4974,7 +5170,7 @@ var PRECOMPILED_CONFIG = {
|
|
|
4974
5170
|
"providers": {
|
|
4975
5171
|
"claude-code": {
|
|
4976
5172
|
"enabled": true,
|
|
4977
|
-
"priority":
|
|
5173
|
+
"priority": 3,
|
|
4978
5174
|
"timeout": 27e5,
|
|
4979
5175
|
"command": "claude",
|
|
4980
5176
|
"healthCheck": {
|
|
@@ -5034,7 +5230,7 @@ var PRECOMPILED_CONFIG = {
|
|
|
5034
5230
|
},
|
|
5035
5231
|
"openai": {
|
|
5036
5232
|
"enabled": true,
|
|
5037
|
-
"priority":
|
|
5233
|
+
"priority": 1,
|
|
5038
5234
|
"timeout": 27e5,
|
|
5039
5235
|
"command": "codex",
|
|
5040
5236
|
"healthCheck": {
|
|
@@ -5272,7 +5468,7 @@ var PRECOMPILED_CONFIG = {
|
|
|
5272
5468
|
"enableFreeTierPrioritization": true,
|
|
5273
5469
|
"enableWorkloadAwareRouting": true
|
|
5274
5470
|
},
|
|
5275
|
-
"version": "11.3.
|
|
5471
|
+
"version": "11.3.3"
|
|
5276
5472
|
};
|
|
5277
5473
|
|
|
5278
5474
|
// src/core/config/schemas.ts
|
|
@@ -5299,7 +5495,7 @@ var safeNameSchema = z.string().min(1).max(VALIDATION_LIMITS.MAX_NAME_LENGTH).re
|
|
|
5299
5495
|
"Name must be alphanumeric with dash/underscore only"
|
|
5300
5496
|
).describe("Safe identifier name");
|
|
5301
5497
|
var relativePathSchema = z.string().min(1).refine(
|
|
5302
|
-
(
|
|
5498
|
+
(path6) => !path6.includes("..") && !path6.startsWith("/"),
|
|
5303
5499
|
"Path must be relative (no ../, no absolute paths)"
|
|
5304
5500
|
).describe("Relative path within project");
|
|
5305
5501
|
var fileExtensionSchema = z.string().regex(
|
|
@@ -5639,16 +5835,16 @@ async function loadConfigUncached(projectDir) {
|
|
|
5639
5835
|
});
|
|
5640
5836
|
return config;
|
|
5641
5837
|
}
|
|
5642
|
-
async function loadConfigFile(
|
|
5838
|
+
async function loadConfigFile(path6) {
|
|
5643
5839
|
try {
|
|
5644
|
-
const content = await readFile(
|
|
5840
|
+
const content = await readFile(path6, "utf-8");
|
|
5645
5841
|
if (content.length > VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE) {
|
|
5646
5842
|
throw ConfigError.parseError(
|
|
5647
5843
|
new Error(`Config file too large (max ${VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE / 1024}KB, got ${Math.ceil(content.length / 1024)}KB)`),
|
|
5648
|
-
|
|
5844
|
+
path6
|
|
5649
5845
|
);
|
|
5650
5846
|
}
|
|
5651
|
-
const ext = extname(
|
|
5847
|
+
const ext = extname(path6).toLowerCase();
|
|
5652
5848
|
let userConfig;
|
|
5653
5849
|
try {
|
|
5654
5850
|
if (ext === ".yaml" || ext === ".yml") {
|
|
@@ -5657,7 +5853,7 @@ async function loadConfigFile(path5) {
|
|
|
5657
5853
|
userConfig = JSON.parse(content);
|
|
5658
5854
|
}
|
|
5659
5855
|
} catch (parseError) {
|
|
5660
|
-
throw ConfigError.parseError(parseError,
|
|
5856
|
+
throw ConfigError.parseError(parseError, path6);
|
|
5661
5857
|
}
|
|
5662
5858
|
const config = mergeConfig(DEFAULT_CONFIG, userConfig);
|
|
5663
5859
|
if (config.execution && userConfig.execution?.maxConcurrentAgents === void 0) {
|
|
@@ -5673,35 +5869,35 @@ async function loadConfigFile(path5) {
|
|
|
5673
5869
|
if (validationErrors.length > 0) {
|
|
5674
5870
|
throw ConfigError.invalid(
|
|
5675
5871
|
validationErrors.join("; "),
|
|
5676
|
-
{ path:
|
|
5872
|
+
{ path: path6, errors: validationErrors }
|
|
5677
5873
|
);
|
|
5678
5874
|
}
|
|
5679
|
-
logger.info("Config loaded successfully", { path: normalizePath(
|
|
5875
|
+
logger.info("Config loaded successfully", { path: normalizePath(path6), format: ext });
|
|
5680
5876
|
return config;
|
|
5681
5877
|
} catch (error) {
|
|
5682
5878
|
if (error instanceof ConfigError) {
|
|
5683
5879
|
throw error;
|
|
5684
5880
|
}
|
|
5685
5881
|
if (error.code === "ENOENT") {
|
|
5686
|
-
throw ConfigError.notFound(
|
|
5882
|
+
throw ConfigError.notFound(path6);
|
|
5687
5883
|
}
|
|
5688
5884
|
if (error.code === "EACCES") {
|
|
5689
5885
|
throw new ConfigError(
|
|
5690
|
-
`Permission denied reading config: ${
|
|
5886
|
+
`Permission denied reading config: ${path6}`,
|
|
5691
5887
|
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
5692
5888
|
[
|
|
5693
5889
|
"Check file permissions",
|
|
5694
5890
|
"Run with appropriate user privileges",
|
|
5695
5891
|
"Verify the file is accessible"
|
|
5696
5892
|
],
|
|
5697
|
-
{ path:
|
|
5893
|
+
{ path: path6, error: error.message }
|
|
5698
5894
|
);
|
|
5699
5895
|
}
|
|
5700
5896
|
throw new ConfigError(
|
|
5701
5897
|
`Failed to load config: ${error.message}`,
|
|
5702
5898
|
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
5703
5899
|
["Check file format and permissions"],
|
|
5704
|
-
{ path:
|
|
5900
|
+
{ path: path6, originalError: error.message }
|
|
5705
5901
|
);
|
|
5706
5902
|
}
|
|
5707
5903
|
}
|
|
@@ -5717,8 +5913,8 @@ function validateConfigWithZod(config) {
|
|
|
5717
5913
|
return ["Configuration validation failed with unknown error structure"];
|
|
5718
5914
|
}
|
|
5719
5915
|
return result.error.issues.map((err) => {
|
|
5720
|
-
const
|
|
5721
|
-
return `${
|
|
5916
|
+
const path6 = err.path.join(".");
|
|
5917
|
+
return `${path6}: ${err.message}`;
|
|
5722
5918
|
});
|
|
5723
5919
|
}
|
|
5724
5920
|
function validateConfig(config) {
|
|
@@ -6128,8 +6324,8 @@ var PathError = class extends Error {
|
|
|
6128
6324
|
};
|
|
6129
6325
|
|
|
6130
6326
|
// src/shared/validation/path-resolver.ts
|
|
6131
|
-
function isWindowsPath(
|
|
6132
|
-
return /^[a-zA-Z]:[/\\]/.test(
|
|
6327
|
+
function isWindowsPath(path6) {
|
|
6328
|
+
return /^[a-zA-Z]:[/\\]/.test(path6);
|
|
6133
6329
|
}
|
|
6134
6330
|
async function detectProjectRoot(startDir = process.cwd()) {
|
|
6135
6331
|
if (process.env.AUTOMATOSX_PROJECT_ROOT) {
|
|
@@ -6229,8 +6425,8 @@ var PathResolver = class {
|
|
|
6229
6425
|
/**
|
|
6230
6426
|
* Validate path is within allowed base directory
|
|
6231
6427
|
*/
|
|
6232
|
-
validatePath(
|
|
6233
|
-
const normalized = normalizePath(resolvePath(
|
|
6428
|
+
validatePath(path6, baseDir) {
|
|
6429
|
+
const normalized = normalizePath(resolvePath(path6));
|
|
6234
6430
|
const base = normalizePath(resolvePath(baseDir));
|
|
6235
6431
|
const separator = "/";
|
|
6236
6432
|
const pathWithSep = normalized + separator;
|
|
@@ -6240,15 +6436,15 @@ var PathResolver = class {
|
|
|
6240
6436
|
/**
|
|
6241
6437
|
* Check if path is within allowed boundaries
|
|
6242
6438
|
*/
|
|
6243
|
-
isPathAllowed(
|
|
6244
|
-
const boundary = this.checkBoundaries(
|
|
6439
|
+
isPathAllowed(path6) {
|
|
6440
|
+
const boundary = this.checkBoundaries(path6);
|
|
6245
6441
|
return boundary === "agent_workspace" || boundary === "user_project";
|
|
6246
6442
|
}
|
|
6247
6443
|
/**
|
|
6248
6444
|
* Check which boundary a path belongs to
|
|
6249
6445
|
*/
|
|
6250
|
-
checkBoundaries(
|
|
6251
|
-
const normalized = resolvePath(
|
|
6446
|
+
checkBoundaries(path6) {
|
|
6447
|
+
const normalized = resolvePath(path6);
|
|
6252
6448
|
if (this.validatePath(normalized, this.config.agentWorkspace)) {
|
|
6253
6449
|
return "agent_workspace";
|
|
6254
6450
|
}
|
|
@@ -6266,15 +6462,15 @@ var PathResolver = class {
|
|
|
6266
6462
|
/**
|
|
6267
6463
|
* Get relative path from project root
|
|
6268
6464
|
*/
|
|
6269
|
-
getRelativeToProject(
|
|
6270
|
-
const normalized = resolvePath(
|
|
6465
|
+
getRelativeToProject(path6) {
|
|
6466
|
+
const normalized = resolvePath(path6);
|
|
6271
6467
|
return normalizePath(getRelativePath(this.config.projectDir, normalized));
|
|
6272
6468
|
}
|
|
6273
6469
|
/**
|
|
6274
6470
|
* Get relative path from working directory
|
|
6275
6471
|
*/
|
|
6276
|
-
getRelativeToWorking(
|
|
6277
|
-
const normalized = resolvePath(
|
|
6472
|
+
getRelativeToWorking(path6) {
|
|
6473
|
+
const normalized = resolvePath(path6);
|
|
6278
6474
|
return normalizePath(getRelativePath(this.config.workingDir, normalized));
|
|
6279
6475
|
}
|
|
6280
6476
|
/**
|
|
@@ -6293,11 +6489,11 @@ var PathResolver = class {
|
|
|
6293
6489
|
* Validate path is within project boundaries
|
|
6294
6490
|
* @throws PathError if outside project
|
|
6295
6491
|
*/
|
|
6296
|
-
validateInProject(
|
|
6297
|
-
const boundary = this.checkBoundaries(
|
|
6492
|
+
validateInProject(path6) {
|
|
6493
|
+
const boundary = this.checkBoundaries(path6);
|
|
6298
6494
|
if (boundary === "outside_boundaries" || boundary === "system_restricted") {
|
|
6299
6495
|
throw new PathError("Path outside project directory", {
|
|
6300
|
-
path:
|
|
6496
|
+
path: path6,
|
|
6301
6497
|
projectDir: this.config.projectDir,
|
|
6302
6498
|
boundary
|
|
6303
6499
|
});
|
|
@@ -8164,18 +8360,18 @@ var ProviderSessionManager = class {
|
|
|
8164
8360
|
};
|
|
8165
8361
|
var providerSessionInstances = /* @__PURE__ */ new Map();
|
|
8166
8362
|
async function getProviderSession(workspacePath) {
|
|
8167
|
-
let
|
|
8363
|
+
let path6;
|
|
8168
8364
|
{
|
|
8169
8365
|
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
8170
|
-
|
|
8366
|
+
path6 = process.cwd();
|
|
8171
8367
|
} else {
|
|
8172
|
-
|
|
8368
|
+
path6 = await detectProjectRoot();
|
|
8173
8369
|
}
|
|
8174
8370
|
}
|
|
8175
|
-
if (!providerSessionInstances.has(
|
|
8176
|
-
providerSessionInstances.set(
|
|
8371
|
+
if (!providerSessionInstances.has(path6)) {
|
|
8372
|
+
providerSessionInstances.set(path6, new ProviderSessionManager(path6));
|
|
8177
8373
|
}
|
|
8178
|
-
return providerSessionInstances.get(
|
|
8374
|
+
return providerSessionInstances.get(path6);
|
|
8179
8375
|
}
|
|
8180
8376
|
|
|
8181
8377
|
// src/core/router/router.ts
|
|
@@ -9944,6 +10140,9 @@ var MemoryManager = class _MemoryManager {
|
|
|
9944
10140
|
if (!Number.isInteger(offsetValue) || offsetValue < 0) {
|
|
9945
10141
|
throw new Error(`Invalid offset value: ${options.offset}. Must be a non-negative integer.`);
|
|
9946
10142
|
}
|
|
10143
|
+
if (!limitClause) {
|
|
10144
|
+
limitClause = "LIMIT -1";
|
|
10145
|
+
}
|
|
9947
10146
|
offsetClause = `OFFSET ${offsetValue}`;
|
|
9948
10147
|
}
|
|
9949
10148
|
const sql = `
|
|
@@ -10297,6 +10496,13 @@ var MemoryManager = class _MemoryManager {
|
|
|
10297
10496
|
await this.initialize();
|
|
10298
10497
|
logger.info("Database restored successfully (atomic operation)", { srcPath: normalizePath(srcPath) });
|
|
10299
10498
|
} catch (error) {
|
|
10499
|
+
try {
|
|
10500
|
+
DatabaseFactory.close(this.db);
|
|
10501
|
+
} finally {
|
|
10502
|
+
this.initialized = false;
|
|
10503
|
+
this.entryCount = 0;
|
|
10504
|
+
this.statements = {};
|
|
10505
|
+
}
|
|
10300
10506
|
throw new MemoryError(
|
|
10301
10507
|
`Failed to restore database: ${error.message}`,
|
|
10302
10508
|
"DATABASE_ERROR",
|
|
@@ -10846,6 +11052,10 @@ var SessionManager = class _SessionManager {
|
|
|
10846
11052
|
MAX_TASKS_PER_SESSION = 1e3;
|
|
10847
11053
|
/** UUID v4 validation regex (static for performance) */
|
|
10848
11054
|
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;
|
|
11055
|
+
/** Tracks whether manager has been destroyed to prevent late saves */
|
|
11056
|
+
destroyed = false;
|
|
11057
|
+
/** Last persistence error (reported during flush) */
|
|
11058
|
+
lastSaveError;
|
|
10849
11059
|
/**
|
|
10850
11060
|
* Validate session ID format (must be valid UUID v4)
|
|
10851
11061
|
*
|
|
@@ -11440,6 +11650,7 @@ var SessionManager = class _SessionManager {
|
|
|
11440
11650
|
* ```
|
|
11441
11651
|
*/
|
|
11442
11652
|
async destroy() {
|
|
11653
|
+
this.destroyed = true;
|
|
11443
11654
|
if (this.saveTimeout) {
|
|
11444
11655
|
clearTimeout(this.saveTimeout);
|
|
11445
11656
|
this.saveTimeout = void 0;
|
|
@@ -11450,9 +11661,12 @@ var SessionManager = class _SessionManager {
|
|
|
11450
11661
|
logger.error("Error flushing save during destroy", {
|
|
11451
11662
|
error: error.message
|
|
11452
11663
|
});
|
|
11664
|
+
this.lastSaveError = void 0;
|
|
11453
11665
|
}
|
|
11666
|
+
const sessionCount = this.activeSessions.size;
|
|
11667
|
+
this.activeSessions.clear();
|
|
11454
11668
|
logger.debug("SessionManager destroyed", {
|
|
11455
|
-
sessions:
|
|
11669
|
+
sessions: sessionCount
|
|
11456
11670
|
});
|
|
11457
11671
|
}
|
|
11458
11672
|
/**
|
|
@@ -11477,11 +11691,21 @@ var SessionManager = class _SessionManager {
|
|
|
11477
11691
|
this.pendingSave = void 0;
|
|
11478
11692
|
});
|
|
11479
11693
|
await this.pendingSave;
|
|
11694
|
+
if (this.lastSaveError) {
|
|
11695
|
+
const err = this.lastSaveError;
|
|
11696
|
+
this.lastSaveError = void 0;
|
|
11697
|
+
throw err;
|
|
11698
|
+
}
|
|
11480
11699
|
return;
|
|
11481
11700
|
}
|
|
11482
11701
|
if (this.pendingSave) {
|
|
11483
11702
|
try {
|
|
11484
11703
|
await this.pendingSave;
|
|
11704
|
+
if (this.lastSaveError) {
|
|
11705
|
+
const err = this.lastSaveError;
|
|
11706
|
+
this.lastSaveError = void 0;
|
|
11707
|
+
throw err;
|
|
11708
|
+
}
|
|
11485
11709
|
} catch (err) {
|
|
11486
11710
|
throw err;
|
|
11487
11711
|
}
|
|
@@ -11646,7 +11870,7 @@ var SessionManager = class _SessionManager {
|
|
|
11646
11870
|
* @private
|
|
11647
11871
|
*/
|
|
11648
11872
|
saveToFile() {
|
|
11649
|
-
if (!this.persistencePath) {
|
|
11873
|
+
if (!this.persistencePath || this.destroyed) {
|
|
11650
11874
|
return;
|
|
11651
11875
|
}
|
|
11652
11876
|
if (this.saveTimeout) {
|
|
@@ -11659,18 +11883,18 @@ var SessionManager = class _SessionManager {
|
|
|
11659
11883
|
}
|
|
11660
11884
|
this.saveNeeded = false;
|
|
11661
11885
|
const executeNextSave = async () => {
|
|
11662
|
-
|
|
11886
|
+
try {
|
|
11887
|
+
await this.doSave();
|
|
11888
|
+
this.lastSaveError = void 0;
|
|
11889
|
+
} catch (err) {
|
|
11890
|
+
this.lastSaveError = err;
|
|
11891
|
+
}
|
|
11663
11892
|
if (this.saveNeeded) {
|
|
11664
11893
|
this.saveNeeded = false;
|
|
11665
11894
|
return executeNextSave();
|
|
11666
11895
|
}
|
|
11667
11896
|
};
|
|
11668
|
-
this.pendingSave = executeNextSave().
|
|
11669
|
-
logger.error("Debounced save failed", {
|
|
11670
|
-
error: err.message
|
|
11671
|
-
});
|
|
11672
|
-
throw err;
|
|
11673
|
-
}).finally(() => {
|
|
11897
|
+
this.pendingSave = executeNextSave().finally(() => {
|
|
11674
11898
|
this.pendingSave = void 0;
|
|
11675
11899
|
});
|
|
11676
11900
|
}, 100);
|
|
@@ -15081,9 +15305,9 @@ var DependencyGraphBuilder = class {
|
|
|
15081
15305
|
detectCycles(graph) {
|
|
15082
15306
|
const visiting = /* @__PURE__ */ new Set();
|
|
15083
15307
|
const visited = /* @__PURE__ */ new Set();
|
|
15084
|
-
const visit = (nodeName,
|
|
15308
|
+
const visit = (nodeName, path6) => {
|
|
15085
15309
|
if (visiting.has(nodeName)) {
|
|
15086
|
-
throw new Error(`Circular dependency detected: ${[...
|
|
15310
|
+
throw new Error(`Circular dependency detected: ${[...path6, nodeName].join(" \u2192 ")}`);
|
|
15087
15311
|
}
|
|
15088
15312
|
if (visited.has(nodeName)) {
|
|
15089
15313
|
return;
|
|
@@ -15098,7 +15322,7 @@ var DependencyGraphBuilder = class {
|
|
|
15098
15322
|
}
|
|
15099
15323
|
visiting.add(nodeName);
|
|
15100
15324
|
for (const dependency of node.dependencies) {
|
|
15101
|
-
visit(dependency, [...
|
|
15325
|
+
visit(dependency, [...path6, nodeName]);
|
|
15102
15326
|
}
|
|
15103
15327
|
visiting.delete(nodeName);
|
|
15104
15328
|
visited.add(nodeName);
|
|
@@ -16831,59 +17055,59 @@ var DANGEROUS_PATH_PATTERNS = [
|
|
|
16831
17055
|
// Common data drive (Windows, alt format)
|
|
16832
17056
|
];
|
|
16833
17057
|
var SUSPICIOUS_PATH_CHARS = /[<>:|"]/;
|
|
16834
|
-
function validatePathParameter(
|
|
16835
|
-
if (!
|
|
17058
|
+
function validatePathParameter(path6, paramName, projectRoot = process.cwd()) {
|
|
17059
|
+
if (!path6 || path6.trim() === "") {
|
|
16836
17060
|
throw new ValidationError(
|
|
16837
17061
|
`Invalid ${paramName}: path cannot be empty`,
|
|
16838
17062
|
-32602 /* InvalidParams */,
|
|
16839
|
-
{ path:
|
|
17063
|
+
{ path: path6, paramName }
|
|
16840
17064
|
);
|
|
16841
17065
|
}
|
|
16842
17066
|
for (const pattern of DANGEROUS_PATH_PATTERNS) {
|
|
16843
|
-
if (
|
|
17067
|
+
if (path6.includes(pattern)) {
|
|
16844
17068
|
throw new ValidationError(
|
|
16845
17069
|
`Invalid ${paramName}: path contains dangerous pattern "${pattern}"`,
|
|
16846
17070
|
-32602 /* InvalidParams */,
|
|
16847
|
-
{ path:
|
|
17071
|
+
{ path: path6, paramName, pattern }
|
|
16848
17072
|
);
|
|
16849
17073
|
}
|
|
16850
17074
|
}
|
|
16851
|
-
if (isAbsolute(
|
|
17075
|
+
if (isAbsolute(path6)) {
|
|
16852
17076
|
throw new ValidationError(
|
|
16853
17077
|
`Invalid ${paramName}: absolute paths are not allowed`,
|
|
16854
17078
|
-32602 /* InvalidParams */,
|
|
16855
|
-
{ path:
|
|
17079
|
+
{ path: path6, paramName }
|
|
16856
17080
|
);
|
|
16857
17081
|
}
|
|
16858
17082
|
try {
|
|
16859
|
-
const resolvedPath = resolve(projectRoot,
|
|
17083
|
+
const resolvedPath = resolve(projectRoot, path6);
|
|
16860
17084
|
const normalizedRoot = resolve(projectRoot);
|
|
16861
17085
|
if (!resolvedPath.startsWith(normalizedRoot + sep) && resolvedPath !== normalizedRoot) {
|
|
16862
17086
|
throw new ValidationError(
|
|
16863
17087
|
`Invalid ${paramName}: path escapes project boundary`,
|
|
16864
17088
|
-32602 /* InvalidParams */,
|
|
16865
|
-
{ path:
|
|
17089
|
+
{ path: path6, paramName, projectRoot, resolvedPath }
|
|
16866
17090
|
);
|
|
16867
17091
|
}
|
|
16868
17092
|
} catch (error) {
|
|
16869
17093
|
throw new ValidationError(
|
|
16870
17094
|
`Invalid ${paramName}: path resolution failed`,
|
|
16871
17095
|
-32602 /* InvalidParams */,
|
|
16872
|
-
{ path:
|
|
17096
|
+
{ path: path6, paramName, error: String(error) }
|
|
16873
17097
|
);
|
|
16874
17098
|
}
|
|
16875
|
-
if (
|
|
17099
|
+
if (path6.includes("\0")) {
|
|
16876
17100
|
throw new ValidationError(
|
|
16877
17101
|
`Invalid ${paramName}: path contains null byte`,
|
|
16878
17102
|
-32602 /* InvalidParams */,
|
|
16879
|
-
{ path:
|
|
17103
|
+
{ path: path6, paramName }
|
|
16880
17104
|
);
|
|
16881
17105
|
}
|
|
16882
|
-
if (SUSPICIOUS_PATH_CHARS.test(
|
|
17106
|
+
if (SUSPICIOUS_PATH_CHARS.test(path6)) {
|
|
16883
17107
|
throw new ValidationError(
|
|
16884
17108
|
`Invalid ${paramName}: path contains invalid characters`,
|
|
16885
17109
|
-32602 /* InvalidParams */,
|
|
16886
|
-
{ path:
|
|
17110
|
+
{ path: path6, paramName }
|
|
16887
17111
|
);
|
|
16888
17112
|
}
|
|
16889
17113
|
}
|
|
@@ -17579,8 +17803,8 @@ function createMemoryExportHandler(deps) {
|
|
|
17579
17803
|
return async (input) => {
|
|
17580
17804
|
logger.info("[MCP] memory_export called", { input });
|
|
17581
17805
|
try {
|
|
17582
|
-
const { path:
|
|
17583
|
-
const absolutePath = resolveExportPath(deps.pathResolver,
|
|
17806
|
+
const { path: path6 } = input;
|
|
17807
|
+
const absolutePath = resolveExportPath(deps.pathResolver, path6);
|
|
17584
17808
|
const exported = await deps.memoryManager.exportToJSON(absolutePath);
|
|
17585
17809
|
const result = {
|
|
17586
17810
|
success: true,
|
|
@@ -17616,8 +17840,8 @@ function createMemoryImportHandler(deps) {
|
|
|
17616
17840
|
return async (input) => {
|
|
17617
17841
|
logger.info("[MCP] memory_import called", { input });
|
|
17618
17842
|
try {
|
|
17619
|
-
const { path:
|
|
17620
|
-
const absolutePath = resolveImportPath(deps.pathResolver,
|
|
17843
|
+
const { path: path6 } = input;
|
|
17844
|
+
const absolutePath = resolveImportPath(deps.pathResolver, path6);
|
|
17621
17845
|
const imported = await deps.memoryManager.importFromJSON(absolutePath);
|
|
17622
17846
|
const result = {
|
|
17623
17847
|
success: true,
|
|
@@ -18170,7 +18394,8 @@ var McpClient = class extends EventEmitter {
|
|
|
18170
18394
|
* Send JSON-RPC request and wait for response
|
|
18171
18395
|
*/
|
|
18172
18396
|
async sendRequest(method, params, timeout) {
|
|
18173
|
-
|
|
18397
|
+
const stdin = this.process?.stdin;
|
|
18398
|
+
if (!stdin) {
|
|
18174
18399
|
throw new Error("MCP client not connected");
|
|
18175
18400
|
}
|
|
18176
18401
|
const id = this.nextRequestId++;
|
|
@@ -18192,7 +18417,18 @@ var McpClient = class extends EventEmitter {
|
|
|
18192
18417
|
timeout: timeoutHandle
|
|
18193
18418
|
});
|
|
18194
18419
|
const message = JSON.stringify(request) + "\n";
|
|
18195
|
-
|
|
18420
|
+
try {
|
|
18421
|
+
stdin.write(message);
|
|
18422
|
+
} catch (error) {
|
|
18423
|
+
const pending = this.pendingRequests.get(id);
|
|
18424
|
+
if (pending) {
|
|
18425
|
+
clearTimeout(pending.timeout);
|
|
18426
|
+
this.pendingRequests.delete(id);
|
|
18427
|
+
}
|
|
18428
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
18429
|
+
reject(err);
|
|
18430
|
+
return;
|
|
18431
|
+
}
|
|
18196
18432
|
logger.debug("[MCP Client] Sent request", {
|
|
18197
18433
|
id,
|
|
18198
18434
|
method,
|
|
@@ -18202,6 +18438,10 @@ var McpClient = class extends EventEmitter {
|
|
|
18202
18438
|
}
|
|
18203
18439
|
/**
|
|
18204
18440
|
* Send JSON-RPC notification (no response expected)
|
|
18441
|
+
*
|
|
18442
|
+
* BUG FIX: Handle write errors gracefully. If the process has exited or the
|
|
18443
|
+
* pipe is broken, write() could throw an uncaught exception that would crash
|
|
18444
|
+
* the application.
|
|
18205
18445
|
*/
|
|
18206
18446
|
sendNotification(method, params) {
|
|
18207
18447
|
if (!this.process?.stdin) {
|
|
@@ -18213,7 +18453,14 @@ var McpClient = class extends EventEmitter {
|
|
|
18213
18453
|
params
|
|
18214
18454
|
};
|
|
18215
18455
|
const message = JSON.stringify(notification) + "\n";
|
|
18216
|
-
|
|
18456
|
+
try {
|
|
18457
|
+
this.process.stdin.write(message);
|
|
18458
|
+
} catch (error) {
|
|
18459
|
+
logger.debug("[MCP Client] Failed to send notification (pipe may be closed)", {
|
|
18460
|
+
method,
|
|
18461
|
+
error: error instanceof Error ? error.message : String(error)
|
|
18462
|
+
});
|
|
18463
|
+
}
|
|
18217
18464
|
}
|
|
18218
18465
|
/**
|
|
18219
18466
|
* Handle incoming JSON-RPC message
|
|
@@ -18564,6 +18811,11 @@ var McpClientPool = class extends EventEmitter {
|
|
|
18564
18811
|
}
|
|
18565
18812
|
/**
|
|
18566
18813
|
* Check if pool has available capacity for provider
|
|
18814
|
+
*
|
|
18815
|
+
* BUG FIX: Account for pendingConnections to avoid reporting capacity
|
|
18816
|
+
* when concurrent connection creations are in progress. Previously,
|
|
18817
|
+
* hasCapacity() could return true even when all slots were about to be
|
|
18818
|
+
* filled by in-flight createConnection() calls, causing over-allocation.
|
|
18567
18819
|
*/
|
|
18568
18820
|
hasCapacity(provider) {
|
|
18569
18821
|
const pool = this.pools.get(provider);
|
|
@@ -18577,7 +18829,8 @@ var McpClientPool = class extends EventEmitter {
|
|
|
18577
18829
|
connectedCount++;
|
|
18578
18830
|
}
|
|
18579
18831
|
}
|
|
18580
|
-
|
|
18832
|
+
const totalAllocated = connectedCount + pool.pendingConnections;
|
|
18833
|
+
return totalAllocated < this.config.maxConnectionsPerProvider;
|
|
18581
18834
|
}
|
|
18582
18835
|
// ============================================
|
|
18583
18836
|
// Private Methods
|
|
@@ -18700,6 +18953,10 @@ var McpClientPool = class extends EventEmitter {
|
|
|
18700
18953
|
}
|
|
18701
18954
|
}
|
|
18702
18955
|
for (const pooledClient of toRemove) {
|
|
18956
|
+
if (pooledClient.inUse) {
|
|
18957
|
+
logger.debug("[MCP Pool] Skipping idle client removal - now in use", { provider });
|
|
18958
|
+
continue;
|
|
18959
|
+
}
|
|
18703
18960
|
logger.debug("[MCP Pool] Closing idle connection", {
|
|
18704
18961
|
provider,
|
|
18705
18962
|
idleTimeMs: now - pooledClient.lastUsed
|