@defai.digital/automatosx 11.3.3 → 11.3.4
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 +16 -0
- package/README.md +1 -1
- package/dist/index.js +718 -403
- package/dist/mcp/index.js +200 -253
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2270,251 +2270,148 @@ function isRateLimitError(error, providerName) {
|
|
|
2270
2270
|
function isLimitError(error, providerName) {
|
|
2271
2271
|
return isQuotaError(error, providerName) || isRateLimitError(error, providerName);
|
|
2272
2272
|
}
|
|
2273
|
-
var PROVIDER_ERROR_PATTERNS, GENERIC_ERROR_PATTERNS;
|
|
2273
|
+
var GEMINI_PATTERNS, CLAUDE_PATTERNS, OPENAI_PATTERNS, AX_CLI_PATTERNS, PROVIDER_ERROR_PATTERNS, GENERIC_ERROR_PATTERNS;
|
|
2274
2274
|
var init_error_patterns = __esm({
|
|
2275
2275
|
"src/providers/error-patterns.ts"() {
|
|
2276
2276
|
init_esm_shims();
|
|
2277
|
+
GEMINI_PATTERNS = {
|
|
2278
|
+
quota: [
|
|
2279
|
+
"RESOURCE_EXHAUSTED",
|
|
2280
|
+
"resource_exhausted",
|
|
2281
|
+
"quotaExceeded",
|
|
2282
|
+
"quota exceeded",
|
|
2283
|
+
"quota limit reached",
|
|
2284
|
+
"daily quota exceeded",
|
|
2285
|
+
"monthly quota exceeded",
|
|
2286
|
+
"insufficient quota"
|
|
2287
|
+
],
|
|
2288
|
+
rateLimit: [
|
|
2289
|
+
"RATE_LIMIT_EXCEEDED",
|
|
2290
|
+
"rate_limit_exceeded",
|
|
2291
|
+
"rate limit exceeded",
|
|
2292
|
+
"too many requests",
|
|
2293
|
+
"requests per minute exceeded",
|
|
2294
|
+
"requests per day exceeded"
|
|
2295
|
+
],
|
|
2296
|
+
statusCodes: [429],
|
|
2297
|
+
// Too Many Requests
|
|
2298
|
+
errorCodes: [
|
|
2299
|
+
"RESOURCE_EXHAUSTED",
|
|
2300
|
+
"RATE_LIMIT_EXCEEDED",
|
|
2301
|
+
"QUOTA_EXCEEDED"
|
|
2302
|
+
]
|
|
2303
|
+
};
|
|
2304
|
+
CLAUDE_PATTERNS = {
|
|
2305
|
+
quota: [
|
|
2306
|
+
"insufficient_quota",
|
|
2307
|
+
"quota_exceeded",
|
|
2308
|
+
"quota exceeded",
|
|
2309
|
+
"credit limit reached",
|
|
2310
|
+
"usage limit exceeded",
|
|
2311
|
+
"monthly quota exceeded"
|
|
2312
|
+
],
|
|
2313
|
+
rateLimit: [
|
|
2314
|
+
"rate_limit_error",
|
|
2315
|
+
"rate limit exceeded",
|
|
2316
|
+
"overloaded_error",
|
|
2317
|
+
"overloaded",
|
|
2318
|
+
"too many requests",
|
|
2319
|
+
"requests per minute exceeded"
|
|
2320
|
+
],
|
|
2321
|
+
statusCodes: [429, 529],
|
|
2322
|
+
// Too Many Requests, Overloaded
|
|
2323
|
+
errorCodes: [
|
|
2324
|
+
"rate_limit_error",
|
|
2325
|
+
"overloaded_error",
|
|
2326
|
+
"insufficient_quota"
|
|
2327
|
+
]
|
|
2328
|
+
};
|
|
2329
|
+
OPENAI_PATTERNS = {
|
|
2330
|
+
quota: [
|
|
2331
|
+
"insufficient_quota",
|
|
2332
|
+
"quota_exceeded",
|
|
2333
|
+
"quota exceeded",
|
|
2334
|
+
"billing hard limit reached",
|
|
2335
|
+
"usage limit exceeded",
|
|
2336
|
+
"monthly quota exceeded",
|
|
2337
|
+
"credit limit reached"
|
|
2338
|
+
],
|
|
2339
|
+
rateLimit: [
|
|
2340
|
+
"rate_limit_exceeded",
|
|
2341
|
+
"rate limit exceeded",
|
|
2342
|
+
"too_many_requests",
|
|
2343
|
+
"too many requests",
|
|
2344
|
+
"requests per minute exceeded",
|
|
2345
|
+
"tokens per minute exceeded",
|
|
2346
|
+
"rate limit reached"
|
|
2347
|
+
],
|
|
2348
|
+
statusCodes: [429],
|
|
2349
|
+
// Too Many Requests
|
|
2350
|
+
errorCodes: [
|
|
2351
|
+
"insufficient_quota",
|
|
2352
|
+
"rate_limit_exceeded",
|
|
2353
|
+
"quota_exceeded",
|
|
2354
|
+
"billing_hard_limit_reached"
|
|
2355
|
+
]
|
|
2356
|
+
};
|
|
2357
|
+
AX_CLI_PATTERNS = {
|
|
2358
|
+
quota: [
|
|
2359
|
+
// GLM patterns
|
|
2360
|
+
"quota exceeded",
|
|
2361
|
+
"quota limit reached",
|
|
2362
|
+
"insufficient quota",
|
|
2363
|
+
// OpenAI patterns (via ax-cli)
|
|
2364
|
+
"insufficient_quota",
|
|
2365
|
+
"quota_exceeded",
|
|
2366
|
+
"billing hard limit reached",
|
|
2367
|
+
"usage limit exceeded",
|
|
2368
|
+
"monthly quota exceeded",
|
|
2369
|
+
"credit limit reached",
|
|
2370
|
+
// Generic patterns
|
|
2371
|
+
"daily quota exceeded",
|
|
2372
|
+
"api quota exceeded"
|
|
2373
|
+
],
|
|
2374
|
+
rateLimit: [
|
|
2375
|
+
// Common patterns
|
|
2376
|
+
"rate_limit_exceeded",
|
|
2377
|
+
"rate limit exceeded",
|
|
2378
|
+
"too_many_requests",
|
|
2379
|
+
"too many requests",
|
|
2380
|
+
"requests per minute exceeded",
|
|
2381
|
+
"tokens per minute exceeded",
|
|
2382
|
+
"rate limit reached",
|
|
2383
|
+
// Anthropic patterns (via ax-cli)
|
|
2384
|
+
"rate_limit_error",
|
|
2385
|
+
"overloaded_error",
|
|
2386
|
+
"overloaded",
|
|
2387
|
+
// xAI/Grok patterns
|
|
2388
|
+
"throttled",
|
|
2389
|
+
"request throttled"
|
|
2390
|
+
],
|
|
2391
|
+
statusCodes: [429, 529],
|
|
2392
|
+
errorCodes: [
|
|
2393
|
+
"insufficient_quota",
|
|
2394
|
+
"rate_limit_exceeded",
|
|
2395
|
+
"quota_exceeded",
|
|
2396
|
+
"rate_limit_error",
|
|
2397
|
+
"overloaded_error",
|
|
2398
|
+
"RATE_LIMIT_EXCEEDED",
|
|
2399
|
+
"QUOTA_EXCEEDED"
|
|
2400
|
+
]
|
|
2401
|
+
};
|
|
2277
2402
|
PROVIDER_ERROR_PATTERNS = {
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
gemini:
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
"quota limit reached",
|
|
2291
|
-
"daily quota exceeded",
|
|
2292
|
-
"monthly quota exceeded",
|
|
2293
|
-
"insufficient quota"
|
|
2294
|
-
],
|
|
2295
|
-
rateLimit: [
|
|
2296
|
-
"RATE_LIMIT_EXCEEDED",
|
|
2297
|
-
"rate_limit_exceeded",
|
|
2298
|
-
"rate limit exceeded",
|
|
2299
|
-
"too many requests",
|
|
2300
|
-
"requests per minute exceeded",
|
|
2301
|
-
"requests per day exceeded"
|
|
2302
|
-
],
|
|
2303
|
-
statusCodes: [429],
|
|
2304
|
-
// Too Many Requests
|
|
2305
|
-
errorCodes: [
|
|
2306
|
-
"RESOURCE_EXHAUSTED",
|
|
2307
|
-
"RATE_LIMIT_EXCEEDED",
|
|
2308
|
-
"QUOTA_EXCEEDED"
|
|
2309
|
-
]
|
|
2310
|
-
},
|
|
2311
|
-
/**
|
|
2312
|
-
* Anthropic Claude Error Patterns
|
|
2313
|
-
*
|
|
2314
|
-
* Claude returns different error types for rate limiting vs overload.
|
|
2315
|
-
* Status 529 indicates temporary overload, 429 indicates rate limiting.
|
|
2316
|
-
*/
|
|
2317
|
-
claude: {
|
|
2318
|
-
quota: [
|
|
2319
|
-
"insufficient_quota",
|
|
2320
|
-
"quota_exceeded",
|
|
2321
|
-
"quota exceeded",
|
|
2322
|
-
"credit limit reached",
|
|
2323
|
-
"usage limit exceeded",
|
|
2324
|
-
"monthly quota exceeded"
|
|
2325
|
-
],
|
|
2326
|
-
rateLimit: [
|
|
2327
|
-
"rate_limit_error",
|
|
2328
|
-
"rate limit exceeded",
|
|
2329
|
-
"overloaded_error",
|
|
2330
|
-
"overloaded",
|
|
2331
|
-
"too many requests",
|
|
2332
|
-
"requests per minute exceeded"
|
|
2333
|
-
],
|
|
2334
|
-
statusCodes: [429, 529],
|
|
2335
|
-
// Too Many Requests, Overloaded
|
|
2336
|
-
errorCodes: [
|
|
2337
|
-
"rate_limit_error",
|
|
2338
|
-
"overloaded_error",
|
|
2339
|
-
"insufficient_quota"
|
|
2340
|
-
]
|
|
2341
|
-
},
|
|
2342
|
-
/**
|
|
2343
|
-
* OpenAI Error Patterns
|
|
2344
|
-
*
|
|
2345
|
-
* OpenAI provides detailed error messages for different types of limits.
|
|
2346
|
-
* Distinguishes between quota exhaustion and rate limiting.
|
|
2347
|
-
*/
|
|
2348
|
-
openai: {
|
|
2349
|
-
quota: [
|
|
2350
|
-
"insufficient_quota",
|
|
2351
|
-
"quota_exceeded",
|
|
2352
|
-
"quota exceeded",
|
|
2353
|
-
"billing hard limit reached",
|
|
2354
|
-
"usage limit exceeded",
|
|
2355
|
-
"monthly quota exceeded",
|
|
2356
|
-
"credit limit reached"
|
|
2357
|
-
],
|
|
2358
|
-
rateLimit: [
|
|
2359
|
-
"rate_limit_exceeded",
|
|
2360
|
-
"rate limit exceeded",
|
|
2361
|
-
"too_many_requests",
|
|
2362
|
-
"too many requests",
|
|
2363
|
-
"requests per minute exceeded",
|
|
2364
|
-
"tokens per minute exceeded",
|
|
2365
|
-
"rate limit reached"
|
|
2366
|
-
],
|
|
2367
|
-
statusCodes: [429],
|
|
2368
|
-
// Too Many Requests
|
|
2369
|
-
errorCodes: [
|
|
2370
|
-
"insufficient_quota",
|
|
2371
|
-
"rate_limit_exceeded",
|
|
2372
|
-
"quota_exceeded",
|
|
2373
|
-
"billing_hard_limit_reached"
|
|
2374
|
-
]
|
|
2375
|
-
},
|
|
2376
|
-
/**
|
|
2377
|
-
* Claude Code Provider (extension of Claude patterns)
|
|
2378
|
-
*/
|
|
2379
|
-
"claude-code": {
|
|
2380
|
-
quota: [
|
|
2381
|
-
"insufficient_quota",
|
|
2382
|
-
"quota_exceeded",
|
|
2383
|
-
"quota exceeded",
|
|
2384
|
-
"credit limit reached",
|
|
2385
|
-
"usage limit exceeded",
|
|
2386
|
-
"monthly quota exceeded"
|
|
2387
|
-
],
|
|
2388
|
-
rateLimit: [
|
|
2389
|
-
"rate_limit_error",
|
|
2390
|
-
"rate limit exceeded",
|
|
2391
|
-
"overloaded_error",
|
|
2392
|
-
"overloaded",
|
|
2393
|
-
"too many requests",
|
|
2394
|
-
"requests per minute exceeded"
|
|
2395
|
-
],
|
|
2396
|
-
statusCodes: [429, 529],
|
|
2397
|
-
errorCodes: [
|
|
2398
|
-
"rate_limit_error",
|
|
2399
|
-
"overloaded_error",
|
|
2400
|
-
"insufficient_quota"
|
|
2401
|
-
]
|
|
2402
|
-
},
|
|
2403
|
-
/**
|
|
2404
|
-
* Gemini CLI Provider (same patterns as Gemini)
|
|
2405
|
-
*/
|
|
2406
|
-
"gemini-cli": {
|
|
2407
|
-
quota: [
|
|
2408
|
-
"RESOURCE_EXHAUSTED",
|
|
2409
|
-
"resource_exhausted",
|
|
2410
|
-
"quotaExceeded",
|
|
2411
|
-
"quota exceeded",
|
|
2412
|
-
"quota limit reached",
|
|
2413
|
-
"daily quota exceeded",
|
|
2414
|
-
"monthly quota exceeded",
|
|
2415
|
-
"insufficient quota"
|
|
2416
|
-
],
|
|
2417
|
-
rateLimit: [
|
|
2418
|
-
"RATE_LIMIT_EXCEEDED",
|
|
2419
|
-
"rate_limit_exceeded",
|
|
2420
|
-
"rate limit exceeded",
|
|
2421
|
-
"too many requests",
|
|
2422
|
-
"requests per minute exceeded",
|
|
2423
|
-
"requests per day exceeded"
|
|
2424
|
-
],
|
|
2425
|
-
statusCodes: [429],
|
|
2426
|
-
errorCodes: [
|
|
2427
|
-
"RESOURCE_EXHAUSTED",
|
|
2428
|
-
"RATE_LIMIT_EXCEEDED",
|
|
2429
|
-
"QUOTA_EXCEEDED"
|
|
2430
|
-
]
|
|
2431
|
-
},
|
|
2432
|
-
/**
|
|
2433
|
-
* Codex CLI Provider (same patterns as OpenAI)
|
|
2434
|
-
*/
|
|
2435
|
-
codex: {
|
|
2436
|
-
quota: [
|
|
2437
|
-
"insufficient_quota",
|
|
2438
|
-
"quota_exceeded",
|
|
2439
|
-
"quota exceeded",
|
|
2440
|
-
"billing hard limit reached",
|
|
2441
|
-
"usage limit exceeded",
|
|
2442
|
-
"monthly quota exceeded",
|
|
2443
|
-
"credit limit reached"
|
|
2444
|
-
],
|
|
2445
|
-
rateLimit: [
|
|
2446
|
-
"rate_limit_exceeded",
|
|
2447
|
-
"rate limit exceeded",
|
|
2448
|
-
"too_many_requests",
|
|
2449
|
-
"too many requests",
|
|
2450
|
-
"requests per minute exceeded",
|
|
2451
|
-
"tokens per minute exceeded",
|
|
2452
|
-
"rate limit reached"
|
|
2453
|
-
],
|
|
2454
|
-
statusCodes: [429],
|
|
2455
|
-
errorCodes: [
|
|
2456
|
-
"insufficient_quota",
|
|
2457
|
-
"rate_limit_exceeded",
|
|
2458
|
-
"quota_exceeded",
|
|
2459
|
-
"billing_hard_limit_reached"
|
|
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
|
-
]
|
|
2517
|
-
}
|
|
2403
|
+
// Primary providers
|
|
2404
|
+
gemini: GEMINI_PATTERNS,
|
|
2405
|
+
claude: CLAUDE_PATTERNS,
|
|
2406
|
+
codex: OPENAI_PATTERNS,
|
|
2407
|
+
"ax-cli": AX_CLI_PATTERNS,
|
|
2408
|
+
// Aliases - reference same patterns to avoid duplication
|
|
2409
|
+
"gemini-cli": GEMINI_PATTERNS,
|
|
2410
|
+
// Alias for gemini
|
|
2411
|
+
"claude-code": CLAUDE_PATTERNS,
|
|
2412
|
+
// Alias for claude
|
|
2413
|
+
"openai": OPENAI_PATTERNS
|
|
2414
|
+
// Alias for codex (same underlying API)
|
|
2518
2415
|
};
|
|
2519
2416
|
GENERIC_ERROR_PATTERNS = {
|
|
2520
2417
|
quota: [
|
|
@@ -2897,9 +2794,14 @@ var init_base_provider = __esm({
|
|
|
2897
2794
|
});
|
|
2898
2795
|
child.kill("SIGTERM");
|
|
2899
2796
|
forceKillTimer = setTimeout(() => {
|
|
2900
|
-
if (child.pid
|
|
2901
|
-
|
|
2902
|
-
|
|
2797
|
+
if (child.pid) {
|
|
2798
|
+
try {
|
|
2799
|
+
process.kill(child.pid, 0);
|
|
2800
|
+
logger.warn("Force killing child process", { pid: child.pid });
|
|
2801
|
+
child.kill("SIGKILL");
|
|
2802
|
+
} catch {
|
|
2803
|
+
logger.debug("Process already exited before SIGKILL", { pid: child.pid });
|
|
2804
|
+
}
|
|
2903
2805
|
}
|
|
2904
2806
|
}, _BaseProvider.SIGKILL_ESCALATION_MS);
|
|
2905
2807
|
}
|
|
@@ -3080,9 +2982,14 @@ var init_base_provider = __esm({
|
|
|
3080
2982
|
});
|
|
3081
2983
|
child.kill("SIGTERM");
|
|
3082
2984
|
forceKillTimer = setTimeout(() => {
|
|
3083
|
-
if (child.pid
|
|
3084
|
-
|
|
3085
|
-
|
|
2985
|
+
if (child.pid) {
|
|
2986
|
+
try {
|
|
2987
|
+
process.kill(child.pid, 0);
|
|
2988
|
+
logger.warn("Force killing child process", { pid: child.pid });
|
|
2989
|
+
child.kill("SIGKILL");
|
|
2990
|
+
} catch {
|
|
2991
|
+
logger.debug("Process already exited before SIGKILL", { pid: child.pid });
|
|
2992
|
+
}
|
|
3086
2993
|
}
|
|
3087
2994
|
}, _BaseProvider.SIGKILL_ESCALATION_MS);
|
|
3088
2995
|
}
|
|
@@ -4361,12 +4268,20 @@ var init_openai_provider = __esm({
|
|
|
4361
4268
|
OpenAIProvider = class extends BaseProvider {
|
|
4362
4269
|
hybridAdapter = null;
|
|
4363
4270
|
providerConfig;
|
|
4271
|
+
/** BUG FIX: Track pending initialization to prevent race condition where
|
|
4272
|
+
* concurrent execute() calls could each trigger initializeHybridAdapter() */
|
|
4273
|
+
initializationPromise = null;
|
|
4274
|
+
/** BUG FIX: Track destroyed state to prevent use-after-destroy race condition */
|
|
4275
|
+
isDestroyed = false;
|
|
4364
4276
|
constructor(config) {
|
|
4365
4277
|
super(config);
|
|
4366
4278
|
this.providerConfig = config;
|
|
4367
4279
|
logger.debug("[OpenAI/Codex] Initialized", { mode: config.mode || "auto" });
|
|
4368
4280
|
}
|
|
4369
4281
|
async execute(request) {
|
|
4282
|
+
if (this.isDestroyed) {
|
|
4283
|
+
throw new Error("OpenAIProvider has been destroyed and cannot execute requests");
|
|
4284
|
+
}
|
|
4370
4285
|
if (process.env.AX_MOCK_PROVIDERS === "true") {
|
|
4371
4286
|
return {
|
|
4372
4287
|
content: this.getMockResponse(),
|
|
@@ -4377,9 +4292,7 @@ var init_openai_provider = __esm({
|
|
|
4377
4292
|
};
|
|
4378
4293
|
}
|
|
4379
4294
|
const startTime = Date.now();
|
|
4380
|
-
|
|
4381
|
-
this.initializeHybridAdapter();
|
|
4382
|
-
}
|
|
4295
|
+
await this.ensureInitialized();
|
|
4383
4296
|
try {
|
|
4384
4297
|
const result = await this.hybridAdapter.execute(
|
|
4385
4298
|
request.prompt,
|
|
@@ -4404,7 +4317,30 @@ var init_openai_provider = __esm({
|
|
|
4404
4317
|
throw this.handleError(error);
|
|
4405
4318
|
}
|
|
4406
4319
|
}
|
|
4407
|
-
|
|
4320
|
+
/**
|
|
4321
|
+
* BUG FIX: Ensure adapter is initialized exactly once, even with concurrent calls.
|
|
4322
|
+
* Uses a promise-based lock pattern to prevent race conditions where multiple
|
|
4323
|
+
* execute() calls could each trigger initializeHybridAdapter().
|
|
4324
|
+
*/
|
|
4325
|
+
async ensureInitialized() {
|
|
4326
|
+
if (this.hybridAdapter) {
|
|
4327
|
+
return;
|
|
4328
|
+
}
|
|
4329
|
+
if (this.initializationPromise) {
|
|
4330
|
+
await this.initializationPromise;
|
|
4331
|
+
return;
|
|
4332
|
+
}
|
|
4333
|
+
this.initializationPromise = this.initializeHybridAdapter();
|
|
4334
|
+
try {
|
|
4335
|
+
await this.initializationPromise;
|
|
4336
|
+
} finally {
|
|
4337
|
+
this.initializationPromise = null;
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
async initializeHybridAdapter() {
|
|
4341
|
+
if (this.hybridAdapter) {
|
|
4342
|
+
return;
|
|
4343
|
+
}
|
|
4408
4344
|
const options = {
|
|
4409
4345
|
mode: this.providerConfig.mode || "auto",
|
|
4410
4346
|
cli: {
|
|
@@ -4442,6 +4378,7 @@ var init_openai_provider = __esm({
|
|
|
4442
4378
|
this.hybridAdapter?.switchToCliMode();
|
|
4443
4379
|
}
|
|
4444
4380
|
async destroy() {
|
|
4381
|
+
this.isDestroyed = true;
|
|
4445
4382
|
if (this.hybridAdapter) {
|
|
4446
4383
|
await this.hybridAdapter.destroy();
|
|
4447
4384
|
this.hybridAdapter = null;
|
|
@@ -5088,6 +5025,8 @@ var init_subagent_adapter = __esm({
|
|
|
5088
5025
|
DEFAULT_TIMEOUT_MS = 3e5;
|
|
5089
5026
|
DEFAULT_SUBAGENT_MAX_TOOL_ROUNDS = 100;
|
|
5090
5027
|
SubagentAdapter = class {
|
|
5028
|
+
// PRODUCTION FIX: Use typed interfaces instead of 'any'
|
|
5029
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5091
5030
|
orchestrator = null;
|
|
5092
5031
|
subagents = /* @__PURE__ */ new Map();
|
|
5093
5032
|
busySubagents = /* @__PURE__ */ new Set();
|
|
@@ -5283,12 +5222,29 @@ ${task.task}` : task.task;
|
|
|
5283
5222
|
});
|
|
5284
5223
|
let content = "";
|
|
5285
5224
|
let totalTokens = 0;
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5225
|
+
const timeoutMs = this.options.timeout;
|
|
5226
|
+
let timeoutTimer = null;
|
|
5227
|
+
try {
|
|
5228
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
5229
|
+
timeoutTimer = setTimeout(() => {
|
|
5230
|
+
reject(new Error(`Subagent task timed out after ${timeoutMs}ms`));
|
|
5231
|
+
}, timeoutMs);
|
|
5232
|
+
});
|
|
5233
|
+
const streamPromise = (async () => {
|
|
5234
|
+
for await (const chunk of subagent.processUserMessageStream(prompt)) {
|
|
5235
|
+
if (chunk.type === "content") {
|
|
5236
|
+
content += chunk.content || "";
|
|
5237
|
+
}
|
|
5238
|
+
if (chunk.type === "token_count" && chunk.tokenCount) {
|
|
5239
|
+
totalTokens += chunk.tokenCount;
|
|
5240
|
+
}
|
|
5241
|
+
}
|
|
5242
|
+
})();
|
|
5243
|
+
await Promise.race([streamPromise, timeoutPromise]);
|
|
5244
|
+
} finally {
|
|
5245
|
+
if (timeoutTimer !== null) {
|
|
5246
|
+
clearTimeout(timeoutTimer);
|
|
5247
|
+
timeoutTimer = null;
|
|
5292
5248
|
}
|
|
5293
5249
|
}
|
|
5294
5250
|
const latencyMs = Date.now() - startTime;
|
|
@@ -5357,7 +5313,7 @@ ${task.task}` : task.task;
|
|
|
5357
5313
|
};
|
|
5358
5314
|
} finally {
|
|
5359
5315
|
if (release) {
|
|
5360
|
-
release();
|
|
5316
|
+
await release();
|
|
5361
5317
|
}
|
|
5362
5318
|
}
|
|
5363
5319
|
}
|
|
@@ -5401,11 +5357,16 @@ ${task.task}` : task.task;
|
|
|
5401
5357
|
}
|
|
5402
5358
|
/**
|
|
5403
5359
|
* Get or create a subagent for the given config
|
|
5360
|
+
*
|
|
5361
|
+
* PRODUCTION FIX: Return typed SDKSubagent instead of 'any'
|
|
5362
|
+
* Note: We still need 'as unknown as SDKSubagent' cast since SDK types may differ,
|
|
5363
|
+
* but this is safer than raw 'any' as it enforces interface compliance.
|
|
5404
5364
|
*/
|
|
5405
5365
|
async getOrCreateSubagent(config) {
|
|
5406
5366
|
const key = this.getSubagentKey(config);
|
|
5407
|
-
|
|
5408
|
-
|
|
5367
|
+
const cached = this.subagents.get(key);
|
|
5368
|
+
if (cached) {
|
|
5369
|
+
return cached;
|
|
5409
5370
|
}
|
|
5410
5371
|
try {
|
|
5411
5372
|
const { createSubagent } = await import('@defai.digital/ax-cli/sdk');
|
|
@@ -5430,6 +5391,8 @@ ${task.task}` : task.task;
|
|
|
5430
5391
|
}
|
|
5431
5392
|
/**
|
|
5432
5393
|
* Create a temporary subagent instance. Used when the cached subagent is busy.
|
|
5394
|
+
*
|
|
5395
|
+
* PRODUCTION FIX: Return typed SDKSubagent instead of 'any'
|
|
5433
5396
|
*/
|
|
5434
5397
|
async createEphemeralSubagent(config) {
|
|
5435
5398
|
const { createSubagent } = await import('@defai.digital/ax-cli/sdk');
|
|
@@ -5442,6 +5405,9 @@ ${task.task}` : task.task;
|
|
|
5442
5405
|
* Acquire a subagent instance for a task. If a cached instance is already
|
|
5443
5406
|
* running another task, create a short-lived one to avoid interleaved
|
|
5444
5407
|
* streams from concurrent executions.
|
|
5408
|
+
*
|
|
5409
|
+
* PRODUCTION FIX (v11.3.4): Changed release() to return Promise for proper
|
|
5410
|
+
* async cleanup of ephemeral subagents. Callers must await release().
|
|
5445
5411
|
*/
|
|
5446
5412
|
async acquireSubagent(config) {
|
|
5447
5413
|
const key = this.getSubagentKey(config);
|
|
@@ -5456,23 +5422,27 @@ ${task.task}` : task.task;
|
|
|
5456
5422
|
}
|
|
5457
5423
|
return {
|
|
5458
5424
|
subagent,
|
|
5459
|
-
release: () => this.releaseSubagent(key)
|
|
5425
|
+
release: async () => this.releaseSubagent(key)
|
|
5460
5426
|
};
|
|
5461
5427
|
}
|
|
5462
5428
|
logger.debug("Cached subagent busy, creating ephemeral instance", { key });
|
|
5463
5429
|
const ephemeral = await this.createEphemeralSubagent(config);
|
|
5464
5430
|
return {
|
|
5465
5431
|
subagent: ephemeral,
|
|
5466
|
-
|
|
5432
|
+
// PRODUCTION FIX: Properly await async dispose to ensure cleanup completes
|
|
5433
|
+
// before next task starts, preventing resource accumulation
|
|
5434
|
+
release: async () => {
|
|
5467
5435
|
try {
|
|
5468
5436
|
if (ephemeral.dispose) {
|
|
5469
5437
|
const result = ephemeral.dispose();
|
|
5470
5438
|
if (result instanceof Promise) {
|
|
5471
|
-
result
|
|
5439
|
+
await result;
|
|
5472
5440
|
}
|
|
5473
5441
|
}
|
|
5442
|
+
logger.debug("Ephemeral subagent disposed successfully", { key });
|
|
5474
5443
|
} catch (error) {
|
|
5475
5444
|
logger.warn("Error disposing ephemeral subagent", {
|
|
5445
|
+
key,
|
|
5476
5446
|
error: error instanceof Error ? error.message : String(error)
|
|
5477
5447
|
});
|
|
5478
5448
|
}
|
|
@@ -5602,7 +5572,7 @@ ${task.task}` : task.task;
|
|
|
5602
5572
|
};
|
|
5603
5573
|
}
|
|
5604
5574
|
});
|
|
5605
|
-
var DEFAULT_CHECKPOINT_DIR, DEFAULT_MAX_CHECKPOINTS, FLUSH_TIMEOUT_MS, CheckpointAdapter;
|
|
5575
|
+
var DEFAULT_CHECKPOINT_DIR, DEFAULT_MAX_CHECKPOINTS, FLUSH_TIMEOUT_MS, FLUSH_MAX_RETRIES, FLUSH_RETRY_DELAY_MS, CheckpointAdapter;
|
|
5606
5576
|
var init_checkpoint_adapter = __esm({
|
|
5607
5577
|
"src/integrations/ax-cli-sdk/checkpoint-adapter.ts"() {
|
|
5608
5578
|
init_esm_shims();
|
|
@@ -5610,6 +5580,8 @@ var init_checkpoint_adapter = __esm({
|
|
|
5610
5580
|
DEFAULT_CHECKPOINT_DIR = ".automatosx/checkpoints";
|
|
5611
5581
|
DEFAULT_MAX_CHECKPOINTS = 10;
|
|
5612
5582
|
FLUSH_TIMEOUT_MS = 5e3;
|
|
5583
|
+
FLUSH_MAX_RETRIES = 3;
|
|
5584
|
+
FLUSH_RETRY_DELAY_MS = 500;
|
|
5613
5585
|
CheckpointAdapter = class {
|
|
5614
5586
|
checkpointDir;
|
|
5615
5587
|
sdkCheckpointManager = null;
|
|
@@ -5781,11 +5753,15 @@ var init_checkpoint_adapter = __esm({
|
|
|
5781
5753
|
let checkpoint = await this.load(workflowId);
|
|
5782
5754
|
const phaseIndex = workflow.phases.findIndex((p) => p.id === phaseId);
|
|
5783
5755
|
if (phaseIndex === -1) {
|
|
5784
|
-
|
|
5756
|
+
const availablePhases = workflow.phases.map((p) => p.id);
|
|
5757
|
+
logger.error("Phase not found in workflow - cannot save checkpoint", {
|
|
5785
5758
|
workflowId,
|
|
5786
5759
|
phaseId,
|
|
5787
|
-
availablePhases
|
|
5760
|
+
availablePhases
|
|
5788
5761
|
});
|
|
5762
|
+
throw new Error(
|
|
5763
|
+
`Invalid phase ID '${phaseId}' for workflow '${workflowId}'. Available phases: ${availablePhases.join(", ")}`
|
|
5764
|
+
);
|
|
5789
5765
|
}
|
|
5790
5766
|
if (!checkpoint) {
|
|
5791
5767
|
checkpoint = {
|
|
@@ -5802,13 +5778,12 @@ var init_checkpoint_adapter = __esm({
|
|
|
5802
5778
|
};
|
|
5803
5779
|
}
|
|
5804
5780
|
if (result.success) {
|
|
5805
|
-
checkpoint.completedTasks.
|
|
5806
|
-
|
|
5807
|
-
checkpoint.phase = phaseIndex + 1;
|
|
5781
|
+
if (!checkpoint.completedTasks.includes(phaseId)) {
|
|
5782
|
+
checkpoint.completedTasks.push(phaseId);
|
|
5808
5783
|
}
|
|
5809
|
-
|
|
5784
|
+
checkpoint.phase = phaseIndex + 1;
|
|
5810
5785
|
checkpoint.context += `
|
|
5811
|
-
[${
|
|
5786
|
+
[Phase ${phaseIndex + 1}: ${phaseId}]
|
|
5812
5787
|
${result.content.substring(0, 1e3)}
|
|
5813
5788
|
`;
|
|
5814
5789
|
if (result.tokensUsed && checkpoint.tokensUsed) {
|
|
@@ -5956,6 +5931,8 @@ ${result.content.substring(0, 1e3)}
|
|
|
5956
5931
|
* Cleanup resources
|
|
5957
5932
|
* BUG FIX: Flush pending checkpoint before destroying to prevent data loss
|
|
5958
5933
|
* BUG FIX: Add timeout to prevent hanging during cleanup
|
|
5934
|
+
* PRODUCTION FIX (v11.3.4): Add retry logic for checkpoint flush to handle
|
|
5935
|
+
* transient filesystem errors (e.g., disk briefly unavailable)
|
|
5959
5936
|
*/
|
|
5960
5937
|
async destroy() {
|
|
5961
5938
|
if (this.autoSaveTimer) {
|
|
@@ -5963,22 +5940,44 @@ ${result.content.substring(0, 1e3)}
|
|
|
5963
5940
|
this.autoSaveTimer = null;
|
|
5964
5941
|
}
|
|
5965
5942
|
if (this.pendingCheckpoint) {
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5943
|
+
const checkpoint = this.pendingCheckpoint;
|
|
5944
|
+
let lastError = null;
|
|
5945
|
+
let success = false;
|
|
5946
|
+
for (let attempt = 1; attempt <= FLUSH_MAX_RETRIES; attempt++) {
|
|
5947
|
+
try {
|
|
5948
|
+
await Promise.race([
|
|
5949
|
+
this.save(checkpoint.workflowId, checkpoint),
|
|
5950
|
+
new Promise(
|
|
5951
|
+
(_, reject) => setTimeout(() => reject(new Error("Checkpoint flush timed out")), FLUSH_TIMEOUT_MS)
|
|
5952
|
+
)
|
|
5953
|
+
]);
|
|
5954
|
+
logger.debug("Pending checkpoint flushed on destroy", {
|
|
5955
|
+
workflowId: checkpoint.workflowId,
|
|
5956
|
+
attempt
|
|
5957
|
+
});
|
|
5958
|
+
success = true;
|
|
5959
|
+
break;
|
|
5960
|
+
} catch (error) {
|
|
5961
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
5962
|
+
logger.warn("Checkpoint flush attempt failed", {
|
|
5963
|
+
workflowId: checkpoint.workflowId,
|
|
5964
|
+
attempt,
|
|
5965
|
+
maxAttempts: FLUSH_MAX_RETRIES,
|
|
5966
|
+
error: lastError.message
|
|
5967
|
+
});
|
|
5968
|
+
if (attempt < FLUSH_MAX_RETRIES) {
|
|
5969
|
+
await new Promise((resolve13) => setTimeout(resolve13, FLUSH_RETRY_DELAY_MS));
|
|
5970
|
+
}
|
|
5971
|
+
}
|
|
5972
|
+
}
|
|
5973
|
+
if (!success && lastError) {
|
|
5974
|
+
logger.error("Failed to flush checkpoint after all retries", {
|
|
5975
|
+
workflowId: checkpoint.workflowId,
|
|
5976
|
+
phase: checkpoint.phase,
|
|
5977
|
+
totalPhases: checkpoint.totalPhases,
|
|
5978
|
+
completedTasks: checkpoint.completedTasks.length,
|
|
5979
|
+
error: lastError.message,
|
|
5980
|
+
hint: "Checkpoint data may need manual recovery"
|
|
5982
5981
|
});
|
|
5983
5982
|
}
|
|
5984
5983
|
this.pendingCheckpoint = null;
|
|
@@ -6225,20 +6224,39 @@ var init_instructions_bridge = __esm({
|
|
|
6225
6224
|
* - Strip quotes from string values
|
|
6226
6225
|
* - Handle empty values correctly
|
|
6227
6226
|
*/
|
|
6227
|
+
/**
|
|
6228
|
+
* PRODUCTION FIX (v11.3.4): Enhanced YAML parser with better edge case handling:
|
|
6229
|
+
* - Handle values containing colons (e.g., "description: Function: does X")
|
|
6230
|
+
* - Strip YAML comments (lines starting with # or inline # comments)
|
|
6231
|
+
* - Preserve escaped quotes within values
|
|
6232
|
+
* - Handle empty/null values correctly
|
|
6233
|
+
*/
|
|
6228
6234
|
parseYamlProfile(content) {
|
|
6229
6235
|
const lines = content.split("\n");
|
|
6230
6236
|
const profile = {};
|
|
6231
6237
|
let currentKey = "";
|
|
6232
6238
|
let instructionsBuffer = [];
|
|
6233
6239
|
let inInstructions = false;
|
|
6234
|
-
for (
|
|
6240
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6241
|
+
const line = lines[i];
|
|
6242
|
+
if (line === void 0) continue;
|
|
6243
|
+
if (line.trim().startsWith("#")) {
|
|
6244
|
+
continue;
|
|
6245
|
+
}
|
|
6235
6246
|
const match = line.match(/^([\w-]+):\s*(.*)$/);
|
|
6236
6247
|
if (match) {
|
|
6237
6248
|
const [, rawKey, rawValue] = match;
|
|
6238
6249
|
const key = rawKey ?? "";
|
|
6239
6250
|
let value = rawValue ?? "";
|
|
6251
|
+
if (!value.startsWith('"') && !value.startsWith("'")) {
|
|
6252
|
+
const commentIndex = value.search(/\s+#/);
|
|
6253
|
+
if (commentIndex > 0) {
|
|
6254
|
+
value = value.substring(0, commentIndex).trim();
|
|
6255
|
+
}
|
|
6256
|
+
}
|
|
6240
6257
|
if (value && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
|
|
6241
6258
|
value = value.slice(1, -1);
|
|
6259
|
+
value = value.replace(/\\"/g, '"').replace(/\\'/g, "'");
|
|
6242
6260
|
}
|
|
6243
6261
|
const normalizedKey = key.replace(/-/g, "_");
|
|
6244
6262
|
if (inInstructions && normalizedKey !== "instructions") {
|
|
@@ -6249,7 +6267,7 @@ var init_instructions_bridge = __esm({
|
|
|
6249
6267
|
if (normalizedKey === "instructions") {
|
|
6250
6268
|
inInstructions = true;
|
|
6251
6269
|
currentKey = normalizedKey;
|
|
6252
|
-
if (value && value.startsWith("|")) ; else if (value) {
|
|
6270
|
+
if (value && (value.startsWith("|") || value.startsWith(">"))) ; else if (value) {
|
|
6253
6271
|
profile.instructions = value;
|
|
6254
6272
|
inInstructions = false;
|
|
6255
6273
|
}
|
|
@@ -6257,14 +6275,23 @@ var init_instructions_bridge = __esm({
|
|
|
6257
6275
|
profile.expertise = [];
|
|
6258
6276
|
currentKey = normalizedKey;
|
|
6259
6277
|
} else if (normalizedKey && value !== void 0) {
|
|
6260
|
-
|
|
6278
|
+
if (value === "null" || value === "~" || value === "") {
|
|
6279
|
+
profile[normalizedKey] = "";
|
|
6280
|
+
} else {
|
|
6281
|
+
profile[normalizedKey] = value;
|
|
6282
|
+
}
|
|
6261
6283
|
currentKey = normalizedKey;
|
|
6262
6284
|
}
|
|
6263
6285
|
} else if (inInstructions) {
|
|
6264
|
-
|
|
6286
|
+
const trimmedLine = line.replace(/^ /, "");
|
|
6287
|
+
instructionsBuffer.push(trimmedLine);
|
|
6265
6288
|
} else if (currentKey === "expertise" && line.trim().startsWith("- ")) {
|
|
6289
|
+
let listValue = line.trim().substring(2);
|
|
6290
|
+
if (listValue.startsWith('"') && listValue.endsWith('"') || listValue.startsWith("'") && listValue.endsWith("'")) {
|
|
6291
|
+
listValue = listValue.slice(1, -1);
|
|
6292
|
+
}
|
|
6266
6293
|
profile.expertise = profile.expertise ?? [];
|
|
6267
|
-
profile.expertise.push(
|
|
6294
|
+
profile.expertise.push(listValue);
|
|
6268
6295
|
}
|
|
6269
6296
|
}
|
|
6270
6297
|
if (inInstructions && instructionsBuffer.length > 0) {
|
|
@@ -6643,7 +6670,7 @@ var init_mcp_manager = __esm({
|
|
|
6643
6670
|
async addFromTemplate(templateName, env) {
|
|
6644
6671
|
const configResult = await this.generateConfigFromTemplate(templateName, env);
|
|
6645
6672
|
if (!configResult.ok) {
|
|
6646
|
-
return configResult;
|
|
6673
|
+
return { ok: false, error: configResult.error };
|
|
6647
6674
|
}
|
|
6648
6675
|
return this.addServer(templateName, configResult.value);
|
|
6649
6676
|
}
|
|
@@ -6653,7 +6680,7 @@ var init_mcp_manager = __esm({
|
|
|
6653
6680
|
async listServers() {
|
|
6654
6681
|
const configResult = await this.loadConfig();
|
|
6655
6682
|
if (!configResult.ok) {
|
|
6656
|
-
return configResult;
|
|
6683
|
+
return { ok: false, error: configResult.error };
|
|
6657
6684
|
}
|
|
6658
6685
|
return { ok: true, value: Object.keys(configResult.value) };
|
|
6659
6686
|
}
|
|
@@ -6706,10 +6733,13 @@ var init_mcp_manager = __esm({
|
|
|
6706
6733
|
const { MCPManagerV2 } = this.mcpModule;
|
|
6707
6734
|
if (MCPManagerV2?.getAllServerStatuses) {
|
|
6708
6735
|
const statuses = MCPManagerV2.getAllServerStatuses();
|
|
6709
|
-
|
|
6710
|
-
|
|
6736
|
+
if (statuses && Array.isArray(statuses)) {
|
|
6737
|
+
for (const status of statuses) {
|
|
6738
|
+
this.serverStatuses.set(status.name, status);
|
|
6739
|
+
}
|
|
6740
|
+
return { ok: true, value: statuses };
|
|
6711
6741
|
}
|
|
6712
|
-
return { ok: true, value:
|
|
6742
|
+
return { ok: true, value: [] };
|
|
6713
6743
|
}
|
|
6714
6744
|
return { ok: true, value: Array.from(this.serverStatuses.values()) };
|
|
6715
6745
|
} catch (error) {
|
|
@@ -6876,7 +6906,7 @@ var init_mcp_manager = __esm({
|
|
|
6876
6906
|
logger.warn("Cannot enumerate servers for shutdown", {
|
|
6877
6907
|
error: serversResult.error.message
|
|
6878
6908
|
});
|
|
6879
|
-
return serversResult;
|
|
6909
|
+
return { ok: false, error: serversResult.error };
|
|
6880
6910
|
}
|
|
6881
6911
|
const errors = [];
|
|
6882
6912
|
for (const name of serversResult.value) {
|
|
@@ -6920,8 +6950,8 @@ var init_adapter2 = __esm({
|
|
|
6920
6950
|
init_mcp_manager();
|
|
6921
6951
|
DEFAULT_MAX_TOOL_ROUNDS = 400;
|
|
6922
6952
|
AxCliSdkAdapter = class _AxCliSdkAdapter {
|
|
6953
|
+
// PRODUCTION FIX: Use typed SDKAgent interface instead of 'any'
|
|
6923
6954
|
agent = null;
|
|
6924
|
-
// Will type as LLMAgent after import
|
|
6925
6955
|
agentConfig = null;
|
|
6926
6956
|
initPromise = null;
|
|
6927
6957
|
executionLock = Promise.resolve();
|
|
@@ -7181,8 +7211,11 @@ var init_adapter2 = __esm({
|
|
|
7181
7211
|
userWantsCallbacks: !!(options.onStream || options.onTool),
|
|
7182
7212
|
note: "SDK handles custom instructions and project memory"
|
|
7183
7213
|
});
|
|
7184
|
-
const chatHistory = typeof this.agent.getChatHistory === "function" ? this.agent.getChatHistory() : null;
|
|
7214
|
+
const chatHistory = this.agent && typeof this.agent.getChatHistory === "function" ? this.agent.getChatHistory() : null;
|
|
7185
7215
|
const historyLengthBefore = Array.isArray(chatHistory) ? chatHistory.length : 0;
|
|
7216
|
+
if (!this.agent) {
|
|
7217
|
+
throw new Error("Agent not initialized");
|
|
7218
|
+
}
|
|
7186
7219
|
for await (const chunk of this.agent.processUserMessageStream(prompt)) {
|
|
7187
7220
|
if (chunk.type === "token_count" && chunk.tokenCount) {
|
|
7188
7221
|
totalTokens += chunk.tokenCount;
|
|
@@ -7219,7 +7252,7 @@ var init_adapter2 = __esm({
|
|
|
7219
7252
|
}
|
|
7220
7253
|
}
|
|
7221
7254
|
}
|
|
7222
|
-
const fullHistory = typeof this.agent.getChatHistory === "function" ? this.agent.getChatHistory() : [];
|
|
7255
|
+
const fullHistory = this.agent && typeof this.agent.getChatHistory === "function" ? this.agent.getChatHistory() : [];
|
|
7223
7256
|
const result = Array.isArray(fullHistory) ? fullHistory.slice(historyLengthBefore) : [];
|
|
7224
7257
|
logger.debug("Chat history extraction", {
|
|
7225
7258
|
historyLengthBefore,
|
|
@@ -7408,7 +7441,7 @@ var init_adapter2 = __esm({
|
|
|
7408
7441
|
});
|
|
7409
7442
|
this.agent.on("error", (error) => {
|
|
7410
7443
|
logger.error("Agent error event", {
|
|
7411
|
-
error: error.message
|
|
7444
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7412
7445
|
});
|
|
7413
7446
|
});
|
|
7414
7447
|
} catch (error) {
|
|
@@ -7499,7 +7532,7 @@ var init_adapter2 = __esm({
|
|
|
7499
7532
|
);
|
|
7500
7533
|
return {
|
|
7501
7534
|
content: content2,
|
|
7502
|
-
model: this.agent?.getCurrentModel() ?? "unknown",
|
|
7535
|
+
model: this.agent?.getCurrentModel?.() ?? "unknown",
|
|
7503
7536
|
tokensUsed: tokensUsed2,
|
|
7504
7537
|
latencyMs,
|
|
7505
7538
|
finishReason: "stop",
|
|
@@ -7516,7 +7549,7 @@ var init_adapter2 = __esm({
|
|
|
7516
7549
|
);
|
|
7517
7550
|
return {
|
|
7518
7551
|
content,
|
|
7519
|
-
model: this.agent?.getCurrentModel() ?? "unknown",
|
|
7552
|
+
model: this.agent?.getCurrentModel?.() ?? "unknown",
|
|
7520
7553
|
tokensUsed,
|
|
7521
7554
|
latencyMs,
|
|
7522
7555
|
finishReason: "stop",
|
|
@@ -7663,9 +7696,11 @@ var init_adapter2 = __esm({
|
|
|
7663
7696
|
this.initError = null;
|
|
7664
7697
|
if (!this.agent) return;
|
|
7665
7698
|
try {
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7699
|
+
if (this.agent.dispose) {
|
|
7700
|
+
const result = this.agent.dispose();
|
|
7701
|
+
if (result instanceof Promise) {
|
|
7702
|
+
await result;
|
|
7703
|
+
}
|
|
7669
7704
|
}
|
|
7670
7705
|
} catch (error) {
|
|
7671
7706
|
logger.warn("Error during agent cleanup", {
|
|
@@ -7695,6 +7730,9 @@ var init_hybrid_adapter2 = __esm({
|
|
|
7695
7730
|
sdkAdapter = null;
|
|
7696
7731
|
cliAdapter = null;
|
|
7697
7732
|
sdkOptions;
|
|
7733
|
+
// BUG FIX (v11.3.4): Track pending initialization to prevent race condition
|
|
7734
|
+
// where concurrent execute() calls could both try to initialize adapters
|
|
7735
|
+
initPromise = null;
|
|
7698
7736
|
constructor(options = {}) {
|
|
7699
7737
|
this.mode = options.mode || "auto";
|
|
7700
7738
|
this.sdkOptions = options.sdk;
|
|
@@ -7771,11 +7809,30 @@ var init_hybrid_adapter2 = __esm({
|
|
|
7771
7809
|
}
|
|
7772
7810
|
/**
|
|
7773
7811
|
* Ensure adapters are initialized based on mode
|
|
7812
|
+
*
|
|
7813
|
+
* BUG FIX (v11.3.4): Use initPromise to prevent race condition where
|
|
7814
|
+
* concurrent execute() calls could both attempt initialization before
|
|
7815
|
+
* activeMode is set. Now subsequent callers wait for the first init.
|
|
7774
7816
|
*/
|
|
7775
7817
|
async ensureAdapters() {
|
|
7776
7818
|
if (this.activeMode !== null) {
|
|
7777
7819
|
return;
|
|
7778
7820
|
}
|
|
7821
|
+
if (this.initPromise) {
|
|
7822
|
+
await this.initPromise;
|
|
7823
|
+
return;
|
|
7824
|
+
}
|
|
7825
|
+
this.initPromise = this.doInitializeAdapters();
|
|
7826
|
+
try {
|
|
7827
|
+
await this.initPromise;
|
|
7828
|
+
} finally {
|
|
7829
|
+
this.initPromise = null;
|
|
7830
|
+
}
|
|
7831
|
+
}
|
|
7832
|
+
/**
|
|
7833
|
+
* Perform actual adapter initialization (called only once via ensureAdapters)
|
|
7834
|
+
*/
|
|
7835
|
+
async doInitializeAdapters() {
|
|
7779
7836
|
if (this.mode === "sdk") {
|
|
7780
7837
|
await this.initializeSdk(true);
|
|
7781
7838
|
this.activeMode = "sdk";
|
|
@@ -27519,13 +27576,17 @@ init_logger();
|
|
|
27519
27576
|
// src/providers/mcp/mcp-client.ts
|
|
27520
27577
|
init_esm_shims();
|
|
27521
27578
|
init_logger();
|
|
27522
|
-
var McpClient = class extends EventEmitter {
|
|
27579
|
+
var McpClient = class _McpClient extends EventEmitter {
|
|
27523
27580
|
config;
|
|
27524
27581
|
process = null;
|
|
27525
27582
|
readline = null;
|
|
27526
27583
|
state;
|
|
27527
27584
|
pendingRequests = /* @__PURE__ */ new Map();
|
|
27528
27585
|
nextRequestId = 1;
|
|
27586
|
+
/** BUG FIX: Maximum request ID before wraparound to prevent Number precision loss.
|
|
27587
|
+
* JavaScript loses integer precision after Number.MAX_SAFE_INTEGER (2^53 - 1).
|
|
27588
|
+
* Using 1 billion as a safe wraparound point - still unique within any realistic session. */
|
|
27589
|
+
static MAX_REQUEST_ID = 1e9;
|
|
27529
27590
|
serverInfo = null;
|
|
27530
27591
|
constructor(config) {
|
|
27531
27592
|
super();
|
|
@@ -27715,6 +27776,9 @@ var McpClient = class extends EventEmitter {
|
|
|
27715
27776
|
throw new Error("MCP client not connected");
|
|
27716
27777
|
}
|
|
27717
27778
|
const id = this.nextRequestId++;
|
|
27779
|
+
if (this.nextRequestId > _McpClient.MAX_REQUEST_ID) {
|
|
27780
|
+
this.nextRequestId = 1;
|
|
27781
|
+
}
|
|
27718
27782
|
const request = {
|
|
27719
27783
|
jsonrpc: "2.0",
|
|
27720
27784
|
id,
|
|
@@ -28045,10 +28109,13 @@ var McpClientPool = class extends EventEmitter {
|
|
|
28045
28109
|
} else {
|
|
28046
28110
|
this.removeFromPool(pool, pooledClient);
|
|
28047
28111
|
this.emitEvent("connection_closed", provider, { reason: "connection_died" });
|
|
28112
|
+
pool.pendingConnections++;
|
|
28048
28113
|
this.createConnection(provider).then((newClient) => {
|
|
28049
28114
|
pool.clients.push(this.createPooledClient(newClient));
|
|
28050
28115
|
waiter.resolve(newClient);
|
|
28051
|
-
}).catch(waiter.reject)
|
|
28116
|
+
}).catch(waiter.reject).finally(() => {
|
|
28117
|
+
pool.pendingConnections--;
|
|
28118
|
+
});
|
|
28052
28119
|
}
|
|
28053
28120
|
}
|
|
28054
28121
|
}
|
|
@@ -35226,7 +35293,7 @@ var IterateError = class extends Error {
|
|
|
35226
35293
|
// src/core/iterate/iterate-classifier.ts
|
|
35227
35294
|
init_esm_shims();
|
|
35228
35295
|
init_logger();
|
|
35229
|
-
var IterateClassifier = class {
|
|
35296
|
+
var IterateClassifier = class _IterateClassifier {
|
|
35230
35297
|
config;
|
|
35231
35298
|
patterns;
|
|
35232
35299
|
/**
|
|
@@ -35307,7 +35374,7 @@ var IterateClassifier = class {
|
|
|
35307
35374
|
reason = "Semantic similarity";
|
|
35308
35375
|
} else {
|
|
35309
35376
|
result = { type: "status_update", confidence: 0.3 };
|
|
35310
|
-
method = "
|
|
35377
|
+
method = "fallback";
|
|
35311
35378
|
reason = "No matches found, using default";
|
|
35312
35379
|
}
|
|
35313
35380
|
}
|
|
@@ -35322,7 +35389,10 @@ var IterateClassifier = class {
|
|
|
35322
35389
|
reason,
|
|
35323
35390
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35324
35391
|
context: {
|
|
35325
|
-
latencyMs: latency
|
|
35392
|
+
latencyMs: latency,
|
|
35393
|
+
// Store the original message for context window lookups
|
|
35394
|
+
// This is used by getRecentMessages() in the controller
|
|
35395
|
+
message
|
|
35326
35396
|
}
|
|
35327
35397
|
};
|
|
35328
35398
|
logger.info("Classification complete", {
|
|
@@ -35453,19 +35523,25 @@ var IterateClassifier = class {
|
|
|
35453
35523
|
* **Phase 1 (Week 1)**: Skeleton only
|
|
35454
35524
|
* **Phase 2 (Week 2)**: Full pattern matching implementation
|
|
35455
35525
|
*/
|
|
35526
|
+
/**
|
|
35527
|
+
* Default priority order for classification types
|
|
35528
|
+
* genuine_question has highest priority to avoid false auto-responses
|
|
35529
|
+
* @private
|
|
35530
|
+
*/
|
|
35531
|
+
static DEFAULT_PRIORITY_ORDER = [
|
|
35532
|
+
"genuine_question",
|
|
35533
|
+
"blocking_request",
|
|
35534
|
+
"rate_limit_or_context",
|
|
35535
|
+
"error_signal",
|
|
35536
|
+
"completion_signal",
|
|
35537
|
+
"confirmation_prompt",
|
|
35538
|
+
"status_update"
|
|
35539
|
+
];
|
|
35456
35540
|
classifyWithPatterns(message) {
|
|
35457
35541
|
if (!this.patterns || this.compiledPatterns.size === 0) {
|
|
35458
35542
|
return null;
|
|
35459
35543
|
}
|
|
35460
|
-
const priorityOrder =
|
|
35461
|
-
"genuine_question",
|
|
35462
|
-
"blocking_request",
|
|
35463
|
-
"rate_limit_or_context",
|
|
35464
|
-
"error_signal",
|
|
35465
|
-
"completion_signal",
|
|
35466
|
-
"confirmation_prompt",
|
|
35467
|
-
"status_update"
|
|
35468
|
-
];
|
|
35544
|
+
const priorityOrder = this.config.classificationPriorityOrder ?? _IterateClassifier.DEFAULT_PRIORITY_ORDER;
|
|
35469
35545
|
for (const type of priorityOrder) {
|
|
35470
35546
|
const patterns = this.compiledPatterns.get(type);
|
|
35471
35547
|
if (!patterns || patterns.length === 0) {
|
|
@@ -35595,28 +35671,71 @@ var IterateClassifier = class {
|
|
|
35595
35671
|
};
|
|
35596
35672
|
}
|
|
35597
35673
|
/**
|
|
35598
|
-
*
|
|
35674
|
+
* Pattern validation errors collected during loading
|
|
35675
|
+
* @private
|
|
35676
|
+
*/
|
|
35677
|
+
patternValidationErrors = [];
|
|
35678
|
+
/**
|
|
35679
|
+
* Compile regex pattern from string with validation
|
|
35599
35680
|
*
|
|
35600
35681
|
* @param patternStr - Pattern string
|
|
35601
|
-
* @
|
|
35682
|
+
* @param classificationType - Type this pattern belongs to (for error reporting)
|
|
35683
|
+
* @returns Compiled RegExp, or null if invalid
|
|
35602
35684
|
* @private
|
|
35603
35685
|
*/
|
|
35604
|
-
compilePattern(patternStr) {
|
|
35686
|
+
compilePattern(patternStr, classificationType) {
|
|
35687
|
+
if (!patternStr || patternStr.trim() === "") {
|
|
35688
|
+
const errorMsg = "Pattern is empty or whitespace-only";
|
|
35689
|
+
logger.warn("Invalid pattern: empty", {
|
|
35690
|
+
type: classificationType,
|
|
35691
|
+
error: errorMsg
|
|
35692
|
+
});
|
|
35693
|
+
this.patternValidationErrors.push({
|
|
35694
|
+
pattern: patternStr || "(empty)",
|
|
35695
|
+
type: classificationType || "unknown",
|
|
35696
|
+
error: errorMsg
|
|
35697
|
+
});
|
|
35698
|
+
return null;
|
|
35699
|
+
}
|
|
35605
35700
|
try {
|
|
35606
35701
|
return new RegExp(patternStr, "i");
|
|
35607
35702
|
} catch (error) {
|
|
35608
|
-
|
|
35609
|
-
|
|
35610
|
-
|
|
35703
|
+
const errorMsg = error.message;
|
|
35704
|
+
logger.warn("Failed to compile pattern - invalid regex syntax", {
|
|
35705
|
+
pattern: patternStr.substring(0, 100),
|
|
35706
|
+
type: classificationType,
|
|
35707
|
+
error: errorMsg
|
|
35611
35708
|
});
|
|
35612
|
-
|
|
35709
|
+
this.patternValidationErrors.push({
|
|
35710
|
+
pattern: patternStr.substring(0, 100),
|
|
35711
|
+
type: classificationType || "unknown",
|
|
35712
|
+
error: errorMsg
|
|
35713
|
+
});
|
|
35714
|
+
return null;
|
|
35613
35715
|
}
|
|
35614
35716
|
}
|
|
35717
|
+
/**
|
|
35718
|
+
* Get pattern validation errors from last load operation
|
|
35719
|
+
*
|
|
35720
|
+
* @returns Array of validation errors with pattern, type, and error message
|
|
35721
|
+
*/
|
|
35722
|
+
getPatternValidationErrors() {
|
|
35723
|
+
return [...this.patternValidationErrors];
|
|
35724
|
+
}
|
|
35725
|
+
/**
|
|
35726
|
+
* Check if there are any pattern validation errors
|
|
35727
|
+
*
|
|
35728
|
+
* @returns True if there are validation errors
|
|
35729
|
+
*/
|
|
35730
|
+
hasPatternValidationErrors() {
|
|
35731
|
+
return this.patternValidationErrors.length > 0;
|
|
35732
|
+
}
|
|
35615
35733
|
/**
|
|
35616
35734
|
* Compile all patterns from pattern library
|
|
35617
35735
|
*
|
|
35618
35736
|
* Compiles regex patterns and caches them for fast matching.
|
|
35619
35737
|
* Patterns are sorted by priority (higher priority first).
|
|
35738
|
+
* Invalid patterns are tracked in patternValidationErrors for reporting.
|
|
35620
35739
|
*
|
|
35621
35740
|
* @private
|
|
35622
35741
|
*/
|
|
@@ -35625,22 +35744,45 @@ var IterateClassifier = class {
|
|
|
35625
35744
|
return;
|
|
35626
35745
|
}
|
|
35627
35746
|
this.compiledPatterns.clear();
|
|
35747
|
+
this.patternValidationErrors = [];
|
|
35628
35748
|
let totalCompiled = 0;
|
|
35749
|
+
let totalFailed = 0;
|
|
35629
35750
|
for (const [type, patterns] of Object.entries(this.patterns.patterns)) {
|
|
35630
35751
|
const classificationType = type;
|
|
35631
35752
|
if (!Array.isArray(patterns) || patterns.length === 0) {
|
|
35632
35753
|
continue;
|
|
35633
35754
|
}
|
|
35634
|
-
const compiled =
|
|
35755
|
+
const compiled = [];
|
|
35756
|
+
for (const p of patterns) {
|
|
35757
|
+
const regex = this.compilePattern(p.pattern, classificationType);
|
|
35758
|
+
if (regex !== null) {
|
|
35759
|
+
compiled.push(regex);
|
|
35760
|
+
totalCompiled++;
|
|
35761
|
+
} else {
|
|
35762
|
+
totalFailed++;
|
|
35763
|
+
}
|
|
35764
|
+
}
|
|
35635
35765
|
if (compiled.length > 0) {
|
|
35636
35766
|
this.compiledPatterns.set(classificationType, compiled);
|
|
35637
|
-
totalCompiled += compiled.length;
|
|
35638
35767
|
}
|
|
35639
35768
|
}
|
|
35640
|
-
|
|
35641
|
-
|
|
35642
|
-
|
|
35643
|
-
|
|
35769
|
+
if (totalFailed > 0) {
|
|
35770
|
+
logger.warn("Pattern compilation completed with errors", {
|
|
35771
|
+
types: this.compiledPatterns.size,
|
|
35772
|
+
totalCompiled,
|
|
35773
|
+
totalFailed,
|
|
35774
|
+
errors: this.patternValidationErrors.map((e) => ({
|
|
35775
|
+
type: e.type,
|
|
35776
|
+
pattern: e.pattern.substring(0, 50),
|
|
35777
|
+
error: e.error
|
|
35778
|
+
}))
|
|
35779
|
+
});
|
|
35780
|
+
} else {
|
|
35781
|
+
logger.debug("Patterns compiled successfully", {
|
|
35782
|
+
types: this.compiledPatterns.size,
|
|
35783
|
+
totalPatterns: totalCompiled
|
|
35784
|
+
});
|
|
35785
|
+
}
|
|
35644
35786
|
}
|
|
35645
35787
|
};
|
|
35646
35788
|
|
|
@@ -36051,14 +36193,22 @@ var IterateModeController = class {
|
|
|
36051
36193
|
* - Safety checks
|
|
36052
36194
|
* - Budget monitoring
|
|
36053
36195
|
*
|
|
36196
|
+
* **Orchestration Flow**:
|
|
36197
|
+
* 1. Initialize state and check initial budgets
|
|
36198
|
+
* 2. Execute agent via provider with hooks installed
|
|
36199
|
+
* 3. When response received, classify and determine action
|
|
36200
|
+
* 4. If 'continue': send auto-response and loop
|
|
36201
|
+
* 5. If 'pause'/'stop': return control to caller
|
|
36202
|
+
*
|
|
36054
36203
|
* @param context - Execution context
|
|
36055
36204
|
* @param options - Execution options
|
|
36205
|
+
* @param executor - Optional agent executor for actual execution (injected for testability)
|
|
36056
36206
|
* @returns Execution result with iterate stats
|
|
36057
36207
|
*
|
|
36058
|
-
*
|
|
36059
|
-
*
|
|
36208
|
+
* @since v6.4.0 - Initial skeleton
|
|
36209
|
+
* @since v11.3.4 - Full implementation with orchestration loop
|
|
36060
36210
|
*/
|
|
36061
|
-
async executeWithIterate(context, options) {
|
|
36211
|
+
async executeWithIterate(context, options, executor) {
|
|
36062
36212
|
await this.ensureInitialized();
|
|
36063
36213
|
const sessionId = this.sessionManager ? (await this.sessionManager.createSession(context.task, context.agent.name)).id : randomUUID();
|
|
36064
36214
|
this.initializeState(sessionId);
|
|
@@ -36083,29 +36233,125 @@ var IterateModeController = class {
|
|
|
36083
36233
|
}
|
|
36084
36234
|
}
|
|
36085
36235
|
});
|
|
36236
|
+
const responseContents = [];
|
|
36237
|
+
let totalTokensUsed = { prompt: 0, completion: 0, total: 0 };
|
|
36238
|
+
let lastResponse;
|
|
36086
36239
|
try {
|
|
36087
36240
|
if (this.checkTimeBudget()) {
|
|
36088
36241
|
throw new IterateError("Time budget exceeded before execution", "budget_exceeded");
|
|
36089
36242
|
}
|
|
36090
|
-
|
|
36091
|
-
|
|
36243
|
+
let currentTask = context.task;
|
|
36244
|
+
let shouldContinue = true;
|
|
36245
|
+
while (shouldContinue) {
|
|
36246
|
+
const iterationContext = {
|
|
36247
|
+
...context,
|
|
36248
|
+
task: currentTask
|
|
36249
|
+
};
|
|
36250
|
+
let result2;
|
|
36251
|
+
let action;
|
|
36252
|
+
if (executor) {
|
|
36253
|
+
let capturedAction;
|
|
36254
|
+
const iterateOptions = {
|
|
36255
|
+
...options,
|
|
36256
|
+
iterateMode: true,
|
|
36257
|
+
hooks: {
|
|
36258
|
+
...options.hooks,
|
|
36259
|
+
onPostResponse: async (response) => {
|
|
36260
|
+
const responseAction = await this.handleResponse(response);
|
|
36261
|
+
capturedAction = responseAction;
|
|
36262
|
+
return responseAction;
|
|
36263
|
+
}
|
|
36264
|
+
}
|
|
36265
|
+
};
|
|
36266
|
+
result2 = await executor.execute(iterationContext, iterateOptions);
|
|
36267
|
+
action = capturedAction || { type: "stop", reason: "No action from executor" };
|
|
36268
|
+
} else {
|
|
36269
|
+
const startTime = Date.now();
|
|
36270
|
+
const response = await context.provider.execute({
|
|
36271
|
+
prompt: currentTask,
|
|
36272
|
+
systemPrompt: context.agent.systemPrompt,
|
|
36273
|
+
model: context.agent.model,
|
|
36274
|
+
temperature: context.agent.temperature,
|
|
36275
|
+
maxTokens: context.agent.maxTokens,
|
|
36276
|
+
signal: options.signal
|
|
36277
|
+
});
|
|
36278
|
+
action = await this.handleResponse(response);
|
|
36279
|
+
result2 = {
|
|
36280
|
+
response,
|
|
36281
|
+
duration: Date.now() - startTime,
|
|
36282
|
+
context: iterationContext
|
|
36283
|
+
};
|
|
36284
|
+
}
|
|
36285
|
+
lastResponse = result2.response;
|
|
36286
|
+
if (result2.response.content) {
|
|
36287
|
+
responseContents.push(result2.response.content);
|
|
36288
|
+
}
|
|
36289
|
+
if (result2.response.tokensUsed) {
|
|
36290
|
+
totalTokensUsed.prompt += result2.response.tokensUsed.prompt || 0;
|
|
36291
|
+
totalTokensUsed.completion += result2.response.tokensUsed.completion || 0;
|
|
36292
|
+
totalTokensUsed.total += result2.response.tokensUsed.total || 0;
|
|
36293
|
+
}
|
|
36294
|
+
logger.debug("Iterate loop action", {
|
|
36295
|
+
actionType: action.type,
|
|
36296
|
+
reason: action.reason,
|
|
36297
|
+
hasAutoResponse: !!action.response
|
|
36298
|
+
});
|
|
36299
|
+
switch (action.type) {
|
|
36300
|
+
case "continue":
|
|
36301
|
+
if (action.response) {
|
|
36302
|
+
currentTask = action.response;
|
|
36303
|
+
this.emitEvent({
|
|
36304
|
+
type: "iterate.auto_response",
|
|
36305
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
36306
|
+
sessionId,
|
|
36307
|
+
payload: {
|
|
36308
|
+
response: action.response,
|
|
36309
|
+
iteration: this.state?.totalIterations || 0
|
|
36310
|
+
}
|
|
36311
|
+
});
|
|
36312
|
+
} else {
|
|
36313
|
+
shouldContinue = false;
|
|
36314
|
+
}
|
|
36315
|
+
break;
|
|
36316
|
+
case "stop":
|
|
36317
|
+
shouldContinue = false;
|
|
36318
|
+
this.emitEvent({
|
|
36319
|
+
type: "iterate.stop",
|
|
36320
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
36321
|
+
sessionId,
|
|
36322
|
+
payload: {
|
|
36323
|
+
reason: action.reason,
|
|
36324
|
+
stats: this.getStats()
|
|
36325
|
+
}
|
|
36326
|
+
});
|
|
36327
|
+
break;
|
|
36328
|
+
case "pause":
|
|
36329
|
+
shouldContinue = false;
|
|
36330
|
+
await this.pause(action.pauseReason || "user_interrupt", action.context);
|
|
36331
|
+
break;
|
|
36332
|
+
case "retry":
|
|
36333
|
+
logger.info("Retry requested, continuing with same task");
|
|
36334
|
+
break;
|
|
36335
|
+
case "no_op":
|
|
36336
|
+
shouldContinue = false;
|
|
36337
|
+
break;
|
|
36338
|
+
default:
|
|
36339
|
+
logger.warn("Unknown action type, stopping iterate loop", { actionType: action.type });
|
|
36340
|
+
shouldContinue = false;
|
|
36341
|
+
}
|
|
36092
36342
|
}
|
|
36093
36343
|
const duration = Date.now() - new Date(this.state.startedAt).getTime();
|
|
36344
|
+
const stats = this.getStats();
|
|
36345
|
+
if (this.statusRenderer) {
|
|
36346
|
+
this.statusRenderer.renderSummary(stats);
|
|
36347
|
+
}
|
|
36094
36348
|
const result = {
|
|
36095
36349
|
response: {
|
|
36096
|
-
content:
|
|
36097
|
-
|
|
36098
|
-
Session ID: ${sessionId}
|
|
36099
|
-
State: ${JSON.stringify(this.getStats(), null, 2)}
|
|
36100
|
-
|
|
36101
|
-
Note: Full execution loop will be implemented in future phases`,
|
|
36102
|
-
tokensUsed: {
|
|
36103
|
-
prompt: 0,
|
|
36104
|
-
completion: 0,
|
|
36105
|
-
total: 0
|
|
36106
|
-
},
|
|
36350
|
+
content: responseContents.join("\n\n---\n\n") || "Iterate mode completed with no output",
|
|
36351
|
+
tokensUsed: totalTokensUsed,
|
|
36107
36352
|
latencyMs: duration,
|
|
36108
|
-
model: context.agent.
|
|
36353
|
+
model: lastResponse?.model || context.agent.model || "unknown",
|
|
36354
|
+
// 'stop' is standard LLM finish reason for both normal completion and pauses
|
|
36109
36355
|
finishReason: "stop"
|
|
36110
36356
|
},
|
|
36111
36357
|
duration,
|
|
@@ -36114,30 +36360,40 @@ Note: Full execution loop will be implemented in future phases`,
|
|
|
36114
36360
|
logger.info("Iterate mode execution completed", {
|
|
36115
36361
|
sessionId,
|
|
36116
36362
|
duration: result.duration,
|
|
36117
|
-
|
|
36363
|
+
iterations: stats.totalIterations,
|
|
36364
|
+
autoResponses: stats.totalAutoResponses,
|
|
36365
|
+
tokens: stats.totalTokens,
|
|
36366
|
+
stopReason: stats.stopReason
|
|
36118
36367
|
});
|
|
36119
36368
|
return result;
|
|
36120
36369
|
} catch (error) {
|
|
36121
36370
|
const duration = this.state ? Date.now() - new Date(this.state.startedAt).getTime() : 0;
|
|
36371
|
+
const stats = this.getStats();
|
|
36122
36372
|
logger.error("Iterate mode execution failed", {
|
|
36123
36373
|
sessionId,
|
|
36124
36374
|
error: error.message,
|
|
36125
|
-
duration
|
|
36375
|
+
duration,
|
|
36376
|
+
stats
|
|
36377
|
+
});
|
|
36378
|
+
this.emitEvent({
|
|
36379
|
+
type: "iterate.error",
|
|
36380
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
36381
|
+
sessionId,
|
|
36382
|
+
payload: {
|
|
36383
|
+
error: error.message,
|
|
36384
|
+
stats
|
|
36385
|
+
}
|
|
36126
36386
|
});
|
|
36127
36387
|
return {
|
|
36128
36388
|
response: {
|
|
36129
|
-
content:
|
|
36130
|
-
|
|
36131
|
-
|
|
36132
|
-
|
|
36133
|
-
|
|
36134
|
-
tokensUsed:
|
|
36135
|
-
prompt: 0,
|
|
36136
|
-
completion: 0,
|
|
36137
|
-
total: 0
|
|
36138
|
-
},
|
|
36389
|
+
content: responseContents.length > 0 ? responseContents.join("\n\n---\n\n") + `
|
|
36390
|
+
|
|
36391
|
+
---
|
|
36392
|
+
|
|
36393
|
+
Iterate mode error: ${error.message}` : `Iterate mode execution failed: ${error.message}`,
|
|
36394
|
+
tokensUsed: totalTokensUsed,
|
|
36139
36395
|
latencyMs: duration,
|
|
36140
|
-
model: context.agent.
|
|
36396
|
+
model: lastResponse?.model || context.agent.model || "unknown",
|
|
36141
36397
|
finishReason: "error"
|
|
36142
36398
|
},
|
|
36143
36399
|
duration,
|
|
@@ -36203,16 +36459,12 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
36203
36459
|
response
|
|
36204
36460
|
});
|
|
36205
36461
|
}
|
|
36206
|
-
if (
|
|
36207
|
-
this.updateTokens(response.tokensUsed.total);
|
|
36208
|
-
}
|
|
36209
|
-
this.incrementIterations();
|
|
36210
|
-
if (this.checkTokenBudget()) {
|
|
36462
|
+
if (this.checkIterationLimit()) {
|
|
36211
36463
|
return {
|
|
36212
36464
|
type: "pause",
|
|
36213
|
-
reason: "
|
|
36214
|
-
pauseReason: "
|
|
36215
|
-
context: `Maximum
|
|
36465
|
+
reason: "Iteration limit exceeded",
|
|
36466
|
+
pauseReason: "iteration_limit_exceeded",
|
|
36467
|
+
context: `Maximum iterations reached`
|
|
36216
36468
|
};
|
|
36217
36469
|
}
|
|
36218
36470
|
if (this.checkTimeBudget()) {
|
|
@@ -36223,14 +36475,19 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
36223
36475
|
context: `Maximum duration of ${this.config.defaults.maxDurationMinutes} minutes reached`
|
|
36224
36476
|
};
|
|
36225
36477
|
}
|
|
36226
|
-
|
|
36478
|
+
const newTokens = response.tokensUsed?.total || 0;
|
|
36479
|
+
if (this.wouldExceedTokenBudget(newTokens)) {
|
|
36227
36480
|
return {
|
|
36228
36481
|
type: "pause",
|
|
36229
|
-
reason: "
|
|
36230
|
-
pauseReason: "
|
|
36231
|
-
context: `Maximum
|
|
36482
|
+
reason: "Token budget exceeded",
|
|
36483
|
+
pauseReason: "token_limit_exceeded",
|
|
36484
|
+
context: `Maximum token limit of ${this.config.defaults.maxTotalTokens} reached`
|
|
36232
36485
|
};
|
|
36233
36486
|
}
|
|
36487
|
+
if (newTokens > 0) {
|
|
36488
|
+
this.updateTokens(newTokens);
|
|
36489
|
+
}
|
|
36490
|
+
this.incrementIterations();
|
|
36234
36491
|
const safetyCheck = this.checkSafetyGuards(response.content || "");
|
|
36235
36492
|
if (!safetyCheck.safe && this.config.notifications.pauseOnHighRiskOperation) {
|
|
36236
36493
|
const dangerousOps = safetyCheck.detectedOperations.map((op) => `${op.type}: ${op.match}`).join(", ");
|
|
@@ -36243,6 +36500,18 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
36243
36500
|
}
|
|
36244
36501
|
const action = this.determineAction(classification, response);
|
|
36245
36502
|
if ((action.type === "continue" || action.type === "stop") && action.response === void 0) {
|
|
36503
|
+
if (this.state && this.state.currentStageAutoResponses >= this.config.defaults.maxAutoResponsesPerStage) {
|
|
36504
|
+
logger.warn("Auto-response limit reached, pausing instead of auto-responding", {
|
|
36505
|
+
currentStageAutoResponses: this.state.currentStageAutoResponses,
|
|
36506
|
+
limit: this.config.defaults.maxAutoResponsesPerStage
|
|
36507
|
+
});
|
|
36508
|
+
return {
|
|
36509
|
+
type: "pause",
|
|
36510
|
+
reason: "Auto-response limit exceeded",
|
|
36511
|
+
pauseReason: "auto_response_limit_exceeded",
|
|
36512
|
+
context: `Maximum auto-responses per stage (${this.config.defaults.maxAutoResponsesPerStage}) reached`
|
|
36513
|
+
};
|
|
36514
|
+
}
|
|
36246
36515
|
const autoResponse = await this.responder.generateResponse(
|
|
36247
36516
|
classification,
|
|
36248
36517
|
{
|
|
@@ -36524,14 +36793,31 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
36524
36793
|
const userInterventions = breakdown.genuine_question + breakdown.blocking_request;
|
|
36525
36794
|
let stopReason = "completion";
|
|
36526
36795
|
if (this.state.pauseReason) {
|
|
36527
|
-
|
|
36528
|
-
|
|
36529
|
-
|
|
36530
|
-
|
|
36531
|
-
|
|
36532
|
-
|
|
36533
|
-
|
|
36534
|
-
|
|
36796
|
+
switch (this.state.pauseReason) {
|
|
36797
|
+
case "time_limit_exceeded":
|
|
36798
|
+
stopReason = "timeout";
|
|
36799
|
+
break;
|
|
36800
|
+
case "token_limit_exceeded":
|
|
36801
|
+
stopReason = "token_limit";
|
|
36802
|
+
break;
|
|
36803
|
+
case "iteration_limit_exceeded":
|
|
36804
|
+
case "auto_response_limit_exceeded":
|
|
36805
|
+
stopReason = "iteration_limit";
|
|
36806
|
+
break;
|
|
36807
|
+
case "user_interrupt":
|
|
36808
|
+
case "genuine_question":
|
|
36809
|
+
case "blocking_request":
|
|
36810
|
+
stopReason = "user_interrupt";
|
|
36811
|
+
break;
|
|
36812
|
+
case "high_risk_operation":
|
|
36813
|
+
stopReason = "safety_paused";
|
|
36814
|
+
break;
|
|
36815
|
+
case "error_recovery_needed":
|
|
36816
|
+
stopReason = "error";
|
|
36817
|
+
break;
|
|
36818
|
+
default:
|
|
36819
|
+
logger.warn("Unknown pauseReason in getStats", { pauseReason: this.state.pauseReason });
|
|
36820
|
+
stopReason = "completion";
|
|
36535
36821
|
}
|
|
36536
36822
|
}
|
|
36537
36823
|
const errorCount = breakdown.error_signal;
|
|
@@ -36625,50 +36911,86 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
36625
36911
|
return true;
|
|
36626
36912
|
}
|
|
36627
36913
|
const percentElapsed = elapsedMinutes / maxMinutes * 100;
|
|
36914
|
+
if (!this.state.metadata.emittedTimeWarnings) {
|
|
36915
|
+
this.state.metadata.emittedTimeWarnings = [];
|
|
36916
|
+
}
|
|
36917
|
+
const emittedTimeWarnings = this.state.metadata.emittedTimeWarnings;
|
|
36628
36918
|
for (const threshold of this.config.notifications.warnAtTimePercent) {
|
|
36629
|
-
if (percentElapsed >= threshold &&
|
|
36919
|
+
if (percentElapsed >= threshold && !emittedTimeWarnings.includes(threshold)) {
|
|
36630
36920
|
logger.warn(`Time budget at ${threshold}% of limit`, {
|
|
36631
36921
|
elapsedMinutes: Math.round(elapsedMinutes),
|
|
36632
36922
|
maxMinutes,
|
|
36633
36923
|
percentUsed: Math.round(percentElapsed)
|
|
36634
36924
|
});
|
|
36925
|
+
emittedTimeWarnings.push(threshold);
|
|
36926
|
+
this.state.lastWarningThreshold = {
|
|
36927
|
+
type: "time",
|
|
36928
|
+
percent: threshold,
|
|
36929
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
36930
|
+
};
|
|
36635
36931
|
}
|
|
36636
36932
|
}
|
|
36637
36933
|
return false;
|
|
36638
36934
|
}
|
|
36639
36935
|
/**
|
|
36640
|
-
* Check if token budget
|
|
36936
|
+
* Check if adding new tokens would exceed the token budget
|
|
36641
36937
|
*
|
|
36642
|
-
* @
|
|
36938
|
+
* @param newTokens - Number of new tokens to add
|
|
36939
|
+
* @returns True if adding these tokens would exceed the budget
|
|
36643
36940
|
* @private
|
|
36644
|
-
* @since
|
|
36941
|
+
* @since v11.3.4
|
|
36645
36942
|
*
|
|
36646
|
-
*
|
|
36943
|
+
* This is a pre-check to prevent exceeding the budget (check BEFORE adding)
|
|
36647
36944
|
*/
|
|
36648
|
-
|
|
36945
|
+
wouldExceedTokenBudget(newTokens) {
|
|
36649
36946
|
if (!this.state) return false;
|
|
36650
36947
|
const maxTotalTokens = this.config.defaults.maxTotalTokens;
|
|
36651
36948
|
if (!maxTotalTokens) {
|
|
36652
36949
|
return false;
|
|
36653
36950
|
}
|
|
36654
36951
|
const currentTokens = this.state.totalTokens;
|
|
36655
|
-
|
|
36656
|
-
|
|
36952
|
+
const projectedTokens = currentTokens + newTokens;
|
|
36953
|
+
if (projectedTokens > maxTotalTokens) {
|
|
36954
|
+
logger.warn("Token budget would be exceeded", {
|
|
36657
36955
|
currentTokens,
|
|
36956
|
+
newTokens,
|
|
36957
|
+
projectedTokens,
|
|
36658
36958
|
maxTotalTokens,
|
|
36659
36959
|
sessionId: this.state.sessionId
|
|
36660
36960
|
});
|
|
36661
36961
|
return true;
|
|
36662
36962
|
}
|
|
36963
|
+
return false;
|
|
36964
|
+
}
|
|
36965
|
+
/**
|
|
36966
|
+
* Check token budget warnings (called after tokens are added)
|
|
36967
|
+
*
|
|
36968
|
+
* @private
|
|
36969
|
+
* @since v8.6.0
|
|
36970
|
+
*
|
|
36971
|
+
* Emits warnings at configured thresholds (default: 75%, 90%)
|
|
36972
|
+
*/
|
|
36973
|
+
checkTokenBudgetWarnings() {
|
|
36974
|
+
if (!this.state) return;
|
|
36975
|
+
const maxTotalTokens = this.config.defaults.maxTotalTokens;
|
|
36976
|
+
if (!maxTotalTokens) {
|
|
36977
|
+
return;
|
|
36978
|
+
}
|
|
36979
|
+
const currentTokens = this.state.totalTokens;
|
|
36663
36980
|
const percentUsed = currentTokens / maxTotalTokens * 100;
|
|
36664
36981
|
const warnThresholds = this.config.defaults.warnAtTokenPercent || [75, 90];
|
|
36982
|
+
if (!this.state.metadata.emittedTokenWarnings) {
|
|
36983
|
+
this.state.metadata.emittedTokenWarnings = [];
|
|
36984
|
+
}
|
|
36985
|
+
const emittedTokenWarnings = this.state.metadata.emittedTokenWarnings;
|
|
36665
36986
|
for (const threshold of warnThresholds) {
|
|
36666
|
-
if (percentUsed >= threshold &&
|
|
36987
|
+
if (percentUsed >= threshold && !emittedTokenWarnings.includes(threshold)) {
|
|
36667
36988
|
logger.warn(`Token budget at ${threshold}% of limit`, {
|
|
36668
36989
|
currentTokens,
|
|
36669
36990
|
maxTotalTokens,
|
|
36670
36991
|
percentUsed: percentUsed.toFixed(1)
|
|
36671
36992
|
});
|
|
36993
|
+
emittedTokenWarnings.push(threshold);
|
|
36672
36994
|
this.state.lastWarningThreshold = {
|
|
36673
36995
|
type: "tokens",
|
|
36674
36996
|
percent: threshold,
|
|
@@ -36676,7 +36998,6 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
36676
36998
|
};
|
|
36677
36999
|
}
|
|
36678
37000
|
}
|
|
36679
|
-
return false;
|
|
36680
37001
|
}
|
|
36681
37002
|
/**
|
|
36682
37003
|
* Check if iteration limit exceeded
|
|
@@ -36705,13 +37026,6 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
36705
37026
|
});
|
|
36706
37027
|
return true;
|
|
36707
37028
|
}
|
|
36708
|
-
if (this.state.currentStageAutoResponses >= this.config.defaults.maxAutoResponsesPerStage) {
|
|
36709
|
-
logger.warn("Auto-response limit exceeded", {
|
|
36710
|
-
currentStageAutoResponses: this.state.currentStageAutoResponses,
|
|
36711
|
-
limit: this.config.defaults.maxAutoResponsesPerStage
|
|
36712
|
-
});
|
|
36713
|
-
return true;
|
|
36714
|
-
}
|
|
36715
37029
|
return false;
|
|
36716
37030
|
}
|
|
36717
37031
|
/**
|
|
@@ -36787,6 +37101,7 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
36787
37101
|
currentStageTokens: this.state.currentStageTokens,
|
|
36788
37102
|
maxTotalTokens: this.config.defaults.maxTotalTokens
|
|
36789
37103
|
});
|
|
37104
|
+
this.checkTokenBudgetWarnings();
|
|
36790
37105
|
}
|
|
36791
37106
|
/**
|
|
36792
37107
|
* Get recent messages from classification history
|
|
@@ -36947,9 +37262,9 @@ State: ${JSON.stringify(this.getStats(), null, 2)}` : ""}`,
|
|
|
36947
37262
|
if (riskTolerance === "permissive") {
|
|
36948
37263
|
return op.risk !== "HIGH";
|
|
36949
37264
|
} else if (riskTolerance === "balanced") {
|
|
36950
|
-
return op.risk
|
|
37265
|
+
return op.risk !== "HIGH";
|
|
36951
37266
|
} else {
|
|
36952
|
-
return
|
|
37267
|
+
return op.risk === "LOW";
|
|
36953
37268
|
}
|
|
36954
37269
|
});
|
|
36955
37270
|
if (detectedOperations.length > 0) {
|