@defai.digital/automatosx 12.3.0 → 12.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +4353 -854
- package/dist/mcp/index.js +3937 -225
- package/package.json +4 -1
package/dist/mcp/index.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as path4 from 'path';
|
|
3
|
-
import path4__default, { dirname, join, extname as extname$1, basename, resolve, relative, isAbsolute, sep,
|
|
3
|
+
import path4__default, { dirname, join, extname as extname$1, basename, resolve, relative, isAbsolute, sep, delimiter, parse } 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
6
|
import * as fs4 from 'fs';
|
|
7
7
|
import { existsSync, readFileSync, promises, mkdirSync, createWriteStream, writeFileSync, unlinkSync, constants } from 'fs';
|
|
8
8
|
import Database2 from 'better-sqlite3';
|
|
9
9
|
import { glob } from 'glob';
|
|
10
|
-
import { spawn, spawnSync } from 'child_process';
|
|
10
|
+
import { spawn, exec, spawnSync } from 'child_process';
|
|
11
11
|
import { z, ZodError } from 'zod';
|
|
12
12
|
import chalk4 from 'chalk';
|
|
13
13
|
import ora2 from 'ora';
|
|
14
14
|
import readline, { createInterface } from 'readline';
|
|
15
15
|
import { Mutex } from 'async-mutex';
|
|
16
|
+
import { promisify } from 'util';
|
|
16
17
|
import Ajv from 'ajv';
|
|
17
18
|
import addFormats from 'ajv-formats';
|
|
18
19
|
import os2, { cpus } from 'os';
|
|
@@ -37,6 +38,226 @@ var init_esm_shims = __esm({
|
|
|
37
38
|
"node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.20.6_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js"() {
|
|
38
39
|
}
|
|
39
40
|
});
|
|
41
|
+
|
|
42
|
+
// src/core/validation-limits.ts
|
|
43
|
+
function isValidRelativePath(path7) {
|
|
44
|
+
if (!path7 || typeof path7 !== "string") {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
const normalizedPath = path7.replace(/\\/g, "/");
|
|
48
|
+
if (normalizedPath.startsWith("/")) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (normalizedPath.includes("..")) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (/^[a-zA-Z]:/.test(normalizedPath)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (normalizedPath.startsWith("//")) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
function isValidCommand(command) {
|
|
63
|
+
if (!command || typeof command !== "string") {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (command.length > VALIDATION_LIMITS.MAX_COMMAND_LENGTH) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
if (!/^[a-z0-9_-]+$/i.test(command)) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
function isValidName(name) {
|
|
75
|
+
if (!name || typeof name !== "string") {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (name.length > VALIDATION_LIMITS.MAX_NAME_LENGTH) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
if (!/^[a-z0-9][a-z0-9-_]*$/i.test(name)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
function isValidExtension(ext) {
|
|
87
|
+
if (!ext || typeof ext !== "string") {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const normalized = ext.startsWith(".") ? ext : `.${ext}`;
|
|
91
|
+
if (normalized.length > 10 || normalized.length < 2) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (!/^\.[a-z0-9]+$/i.test(normalized)) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
function isPositiveInteger(value) {
|
|
100
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
101
|
+
}
|
|
102
|
+
function isNonNegativeInteger(value) {
|
|
103
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
104
|
+
}
|
|
105
|
+
var AX_PATHS, TIMEOUTS, DATABASE, VALIDATION_LIMITS;
|
|
106
|
+
var init_validation_limits = __esm({
|
|
107
|
+
"src/core/validation-limits.ts"() {
|
|
108
|
+
init_esm_shims();
|
|
109
|
+
AX_PATHS = {
|
|
110
|
+
/** Root directory for AutomatosX data */
|
|
111
|
+
ROOT: ".automatosx",
|
|
112
|
+
/** Log files directory */
|
|
113
|
+
LOGS: ".automatosx/logs",
|
|
114
|
+
/** Memory/database directory */
|
|
115
|
+
MEMORY: ".automatosx/memory",
|
|
116
|
+
/** Agent workspaces directory */
|
|
117
|
+
WORKSPACES: ".automatosx/workspaces",
|
|
118
|
+
/** Session data directory */
|
|
119
|
+
SESSIONS: ".automatosx/sessions",
|
|
120
|
+
/** Team configurations directory */
|
|
121
|
+
TEAMS: ".automatosx/teams",
|
|
122
|
+
/** Agent profiles directory */
|
|
123
|
+
AGENTS: ".automatosx/agents",
|
|
124
|
+
/** Workflow definitions directory */
|
|
125
|
+
WORKFLOWS: ".automatosx/workflows",
|
|
126
|
+
/** Abilities/skills directory */
|
|
127
|
+
ABILITIES: ".automatosx/abilities",
|
|
128
|
+
/** Checkpoints directory */
|
|
129
|
+
CHECKPOINTS: ".automatosx/checkpoints",
|
|
130
|
+
/** Cache directory */
|
|
131
|
+
CACHE: ".automatosx/cache",
|
|
132
|
+
/** Task database directory */
|
|
133
|
+
TASKS: ".automatosx/tasks",
|
|
134
|
+
/** Status files directory */
|
|
135
|
+
STATUS: ".automatosx/status",
|
|
136
|
+
/** Provider configurations directory */
|
|
137
|
+
PROVIDERS: ".automatosx/providers",
|
|
138
|
+
/** Iterate mode directory */
|
|
139
|
+
ITERATE: ".automatosx/iterate",
|
|
140
|
+
/** State directory (mode, provider overrides) */
|
|
141
|
+
STATE: ".automatosx/state",
|
|
142
|
+
/** Usage tracking directory */
|
|
143
|
+
USAGE: ".automatosx/usage",
|
|
144
|
+
/** Context store directory */
|
|
145
|
+
CONTEXT: ".automatosx/context",
|
|
146
|
+
/** Telemetry directory */
|
|
147
|
+
TELEMETRY: ".automatosx/telemetry",
|
|
148
|
+
/** Workspace index directory */
|
|
149
|
+
WORKSPACE: ".automatosx/workspace",
|
|
150
|
+
/** Config file paths */
|
|
151
|
+
CONFIG_YAML: ".automatosx/config.yaml",
|
|
152
|
+
CONFIG_JSON: ".automatosx/config.json"
|
|
153
|
+
};
|
|
154
|
+
TIMEOUTS = {
|
|
155
|
+
/** Default provider execution timeout (2 minutes) */
|
|
156
|
+
PROVIDER_DEFAULT: 12e4,
|
|
157
|
+
/** Provider detection/health check timeout (5 seconds) */
|
|
158
|
+
PROVIDER_DETECTION: 5e3,
|
|
159
|
+
/** Database busy timeout (5 seconds) */
|
|
160
|
+
DATABASE_BUSY: 5e3,
|
|
161
|
+
/** Provider cooldown after failure (30 seconds) */
|
|
162
|
+
PROVIDER_COOLDOWN: 3e4,
|
|
163
|
+
/** Circuit breaker recovery timeout (1 minute) */
|
|
164
|
+
CIRCUIT_BREAKER_RECOVERY: 6e4,
|
|
165
|
+
/** Circuit breaker failure window (5 minutes) */
|
|
166
|
+
CIRCUIT_BREAKER_WINDOW: 3e5,
|
|
167
|
+
/** Health check interval (5 minutes) */
|
|
168
|
+
HEALTH_CHECK_INTERVAL: 3e5,
|
|
169
|
+
/** Idle connection timeout (5 minutes) */
|
|
170
|
+
IDLE_CONNECTION: 3e5,
|
|
171
|
+
/** User decision/prompt timeout (10 minutes) */
|
|
172
|
+
USER_DECISION: 6e5,
|
|
173
|
+
/** Maximum execution timeout (1 hour) */
|
|
174
|
+
MAX_EXECUTION: 36e5,
|
|
175
|
+
/** Default global timeout (25 minutes) */
|
|
176
|
+
GLOBAL_DEFAULT: 15e5,
|
|
177
|
+
/** Config cache TTL (1 minute) */
|
|
178
|
+
CONFIG_CACHE_TTL: 6e4,
|
|
179
|
+
/** Score cache TTL (5 minutes) */
|
|
180
|
+
SCORE_CACHE_TTL: 3e5,
|
|
181
|
+
/** MCP health check interval (30 seconds) */
|
|
182
|
+
MCP_HEALTH_CHECK: 3e4,
|
|
183
|
+
/** CLI command confirmation delay (5 seconds) */
|
|
184
|
+
CLI_CONFIRM_DELAY: 5e3,
|
|
185
|
+
/** Kill switch confirmation delay (5 seconds) */
|
|
186
|
+
KILL_SWITCH_DELAY: 5e3,
|
|
187
|
+
/** Minimum rollout duration (1 hour) */
|
|
188
|
+
MIN_ROLLOUT_DURATION: 36e5,
|
|
189
|
+
/** Graceful shutdown timeout (30 seconds) */
|
|
190
|
+
GRACEFUL_SHUTDOWN: 3e4,
|
|
191
|
+
/** Package installation timeout (1 minute) */
|
|
192
|
+
PACKAGE_INSTALL: 6e4,
|
|
193
|
+
/** Quick CLI command timeout (3 seconds) */
|
|
194
|
+
QUICK_COMMAND: 3e3,
|
|
195
|
+
/** CLI version check timeout (10 seconds) */
|
|
196
|
+
VERSION_CHECK: 1e4
|
|
197
|
+
};
|
|
198
|
+
DATABASE = {
|
|
199
|
+
/** SQLite busy timeout in milliseconds */
|
|
200
|
+
BUSY_TIMEOUT: 5e3,
|
|
201
|
+
/** Maximum database connections in pool */
|
|
202
|
+
MAX_CONNECTIONS: 10,
|
|
203
|
+
/** Connection idle timeout (5 minutes) */
|
|
204
|
+
IDLE_TIMEOUT: 3e5,
|
|
205
|
+
/** Default query limit */
|
|
206
|
+
DEFAULT_QUERY_LIMIT: 1e3,
|
|
207
|
+
/** Maximum query limit */
|
|
208
|
+
MAX_QUERY_LIMIT: 1e4
|
|
209
|
+
};
|
|
210
|
+
VALIDATION_LIMITS = {
|
|
211
|
+
// Resource limits (prevent DoS)
|
|
212
|
+
MAX_ENTRIES: 1e6,
|
|
213
|
+
// 1 million entries (memory, cache)
|
|
214
|
+
MAX_TIMEOUT: 36e5,
|
|
215
|
+
// 1 hour (execution, delegation)
|
|
216
|
+
MAX_FILE_SIZE: 104857600,
|
|
217
|
+
// 100 MB (workspace, abilities)
|
|
218
|
+
MAX_CACHE_SIZE: 524288e3,
|
|
219
|
+
// 500 MB (cache storage)
|
|
220
|
+
MAX_SESSIONS: 1e4,
|
|
221
|
+
// 10k concurrent sessions
|
|
222
|
+
// Performance limits (prevent resource exhaustion)
|
|
223
|
+
MAX_CONCURRENT_AGENTS: 100,
|
|
224
|
+
// Maximum concurrent agents
|
|
225
|
+
MAX_CPU_PERCENT: 80,
|
|
226
|
+
// Maximum CPU usage percent
|
|
227
|
+
MAX_MEMORY_MB: 2048,
|
|
228
|
+
// Maximum memory usage in MB
|
|
229
|
+
// Config validation limits
|
|
230
|
+
MAX_CONFIG_FILE_SIZE: 1048576,
|
|
231
|
+
// 1MB max config file size
|
|
232
|
+
MAX_NAME_LENGTH: 50,
|
|
233
|
+
// Max name length for agents/providers
|
|
234
|
+
MAX_COMMAND_LENGTH: 100,
|
|
235
|
+
// Max command length
|
|
236
|
+
MAX_ARRAY_LENGTH: 1e4,
|
|
237
|
+
// Max array elements in config
|
|
238
|
+
// Path and file limits
|
|
239
|
+
MIN_FILE_SIZE: 1,
|
|
240
|
+
// Minimum file size in bytes
|
|
241
|
+
MIN_TIMEOUT: 1e3,
|
|
242
|
+
// Minimum timeout in ms (1 second)
|
|
243
|
+
MIN_INTERVAL: 100,
|
|
244
|
+
// Minimum interval in ms
|
|
245
|
+
MIN_BACKOFF_FACTOR: 1,
|
|
246
|
+
// Minimum backoff multiplier
|
|
247
|
+
MAX_BACKOFF_FACTOR: 5,
|
|
248
|
+
// Maximum backoff multiplier
|
|
249
|
+
MAX_TTL: 864e5,
|
|
250
|
+
// Maximum TTL in ms (24 hours)
|
|
251
|
+
MIN_BYTES: 1,
|
|
252
|
+
// Minimum bytes for file sizes
|
|
253
|
+
// Network limits
|
|
254
|
+
MIN_PORT: 1024,
|
|
255
|
+
// Minimum port number
|
|
256
|
+
MAX_PORT: 65535
|
|
257
|
+
// Maximum port number
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
});
|
|
40
261
|
function sanitizeObject(obj, maxDepth = 5, currentDepth = 0) {
|
|
41
262
|
if (currentDepth > maxDepth) {
|
|
42
263
|
return "[Max Depth Reached]";
|
|
@@ -581,7 +802,7 @@ function installExitHandlers() {
|
|
|
581
802
|
if (process.stderr.writable) {
|
|
582
803
|
process.stderr.end();
|
|
583
804
|
}
|
|
584
|
-
} catch (
|
|
805
|
+
} catch (_error) {
|
|
585
806
|
}
|
|
586
807
|
process.exit(signal === "SIGTERM" || signal === "SIGINT" ? 0 : 1);
|
|
587
808
|
};
|
|
@@ -602,6 +823,7 @@ var init_process_manager = __esm({
|
|
|
602
823
|
"src/shared/process/process-manager.ts"() {
|
|
603
824
|
init_esm_shims();
|
|
604
825
|
init_logger();
|
|
826
|
+
init_validation_limits();
|
|
605
827
|
ProcessManager = class {
|
|
606
828
|
childProcesses = /* @__PURE__ */ new Set();
|
|
607
829
|
isShuttingDown = false;
|
|
@@ -638,7 +860,7 @@ var init_process_manager = __esm({
|
|
|
638
860
|
*
|
|
639
861
|
* @param timeout Maximum time to wait for graceful shutdown (ms)
|
|
640
862
|
*/
|
|
641
|
-
async shutdown(timeout =
|
|
863
|
+
async shutdown(timeout = TIMEOUTS.PROVIDER_DETECTION) {
|
|
642
864
|
if (this.isShuttingDown) {
|
|
643
865
|
return;
|
|
644
866
|
}
|
|
@@ -767,9 +989,10 @@ var init_factory = __esm({
|
|
|
767
989
|
"src/core/database/factory.ts"() {
|
|
768
990
|
init_esm_shims();
|
|
769
991
|
init_logger();
|
|
992
|
+
init_validation_limits();
|
|
770
993
|
DEFAULT_OPTIONS = {
|
|
771
994
|
readonly: false,
|
|
772
|
-
busyTimeout:
|
|
995
|
+
busyTimeout: DATABASE.BUSY_TIMEOUT,
|
|
773
996
|
verbose: false,
|
|
774
997
|
enableWal: true,
|
|
775
998
|
createDir: true
|
|
@@ -1615,8 +1838,8 @@ var init_verbosity_manager = __esm({
|
|
|
1615
1838
|
}
|
|
1616
1839
|
});
|
|
1617
1840
|
function findOnPath(cmdBase) {
|
|
1618
|
-
const
|
|
1619
|
-
if (
|
|
1841
|
+
const isWindows = process.platform === "win32";
|
|
1842
|
+
if (isWindows) {
|
|
1620
1843
|
return findOnPathWindows(cmdBase);
|
|
1621
1844
|
}
|
|
1622
1845
|
return findOnPathUnix(cmdBase);
|
|
@@ -1964,9 +2187,10 @@ var init_streaming_progress_parser = __esm({
|
|
|
1964
2187
|
// src/providers/error-patterns.ts
|
|
1965
2188
|
function isQuotaError(error, providerName) {
|
|
1966
2189
|
const patterns = PROVIDER_ERROR_PATTERNS[providerName] || GENERIC_ERROR_PATTERNS;
|
|
1967
|
-
const
|
|
1968
|
-
const
|
|
1969
|
-
const
|
|
2190
|
+
const errorObj = error;
|
|
2191
|
+
const message = (errorObj?.message || "").toLowerCase();
|
|
2192
|
+
const code = errorObj?.code || "";
|
|
2193
|
+
const statusCode = errorObj?.status || errorObj?.statusCode;
|
|
1970
2194
|
if (patterns.quota.some((pattern) => message.includes(pattern.toLowerCase()))) {
|
|
1971
2195
|
return true;
|
|
1972
2196
|
}
|
|
@@ -1983,9 +2207,10 @@ function isQuotaError(error, providerName) {
|
|
|
1983
2207
|
}
|
|
1984
2208
|
function isRateLimitError(error, providerName) {
|
|
1985
2209
|
const patterns = PROVIDER_ERROR_PATTERNS[providerName] || GENERIC_ERROR_PATTERNS;
|
|
1986
|
-
const
|
|
1987
|
-
const
|
|
1988
|
-
const
|
|
2210
|
+
const errorObj = error;
|
|
2211
|
+
const message = (errorObj?.message || "").toLowerCase();
|
|
2212
|
+
const code = errorObj?.code || "";
|
|
2213
|
+
const statusCode = errorObj?.status || errorObj?.statusCode;
|
|
1989
2214
|
if (patterns.rateLimit.some((pattern) => message.includes(pattern.toLowerCase()))) {
|
|
1990
2215
|
return true;
|
|
1991
2216
|
}
|
|
@@ -2224,6 +2449,7 @@ var init_base_provider = __esm({
|
|
|
2224
2449
|
init_esm_shims();
|
|
2225
2450
|
init_logger();
|
|
2226
2451
|
init_errors();
|
|
2452
|
+
init_validation_limits();
|
|
2227
2453
|
init_cli_provider_detector();
|
|
2228
2454
|
init_provider_schemas();
|
|
2229
2455
|
init_streaming_progress_parser();
|
|
@@ -2266,15 +2492,16 @@ var init_base_provider = __esm({
|
|
|
2266
2492
|
DEBIAN_FRONTEND: "noninteractive"
|
|
2267
2493
|
};
|
|
2268
2494
|
/** Default CLI execution timeout in milliseconds */
|
|
2269
|
-
static DEFAULT_TIMEOUT_MS =
|
|
2495
|
+
static DEFAULT_TIMEOUT_MS = TIMEOUTS.PROVIDER_DEFAULT;
|
|
2270
2496
|
/** Time to wait after SIGTERM before escalating to SIGKILL */
|
|
2271
|
-
static SIGKILL_ESCALATION_MS =
|
|
2497
|
+
static SIGKILL_ESCALATION_MS = TIMEOUTS.PROVIDER_DETECTION;
|
|
2272
2498
|
config;
|
|
2273
2499
|
logger = logger;
|
|
2274
2500
|
health;
|
|
2275
2501
|
constructor(config) {
|
|
2276
2502
|
const providerName = config.name.toLowerCase();
|
|
2277
|
-
|
|
2503
|
+
const allowedNames = _BaseProvider.ALLOWED_PROVIDER_NAMES;
|
|
2504
|
+
if (!allowedNames.includes(providerName)) {
|
|
2278
2505
|
throw new ProviderError(
|
|
2279
2506
|
`Invalid provider name: ${config.name}. Allowed: ${_BaseProvider.ALLOWED_PROVIDER_NAMES.join(", ")}`,
|
|
2280
2507
|
"E1001" /* CONFIG_INVALID */
|
|
@@ -2319,8 +2546,8 @@ var init_base_provider = __esm({
|
|
|
2319
2546
|
const escapedPrompt = this.escapeShellArg(prompt);
|
|
2320
2547
|
const fullCommand = `${cliCommand} ${argsString}${escapedPrompt}`;
|
|
2321
2548
|
const commandLength = fullCommand.length;
|
|
2322
|
-
const
|
|
2323
|
-
const useStdin =
|
|
2549
|
+
const isWindows = process.platform === "win32";
|
|
2550
|
+
const useStdin = isWindows && commandLength > 7e3;
|
|
2324
2551
|
logger.debug(`Executing ${cliCommand} CLI with streaming support`, {
|
|
2325
2552
|
command: cliCommand,
|
|
2326
2553
|
promptLength: prompt.length,
|
|
@@ -2909,8 +3136,8 @@ ${fullPrompt}
|
|
|
2909
3136
|
* - Windows: hello"world → "hello\"world"
|
|
2910
3137
|
*/
|
|
2911
3138
|
escapeShellArg(arg) {
|
|
2912
|
-
const
|
|
2913
|
-
if (
|
|
3139
|
+
const isWindows = process.platform === "win32";
|
|
3140
|
+
if (isWindows) {
|
|
2914
3141
|
return '"' + arg.replace(/"/g, '\\"').replace(/%/g, "%%") + '"';
|
|
2915
3142
|
} else {
|
|
2916
3143
|
return "'" + arg.replace(/'/g, "'\\''") + "'";
|
|
@@ -3778,7 +4005,7 @@ var init_sdk_adapter = __esm({
|
|
|
3778
4005
|
this.codex = new this.sdkModule.Codex();
|
|
3779
4006
|
this.initialized = true;
|
|
3780
4007
|
logger.info("Codex SDK initialized");
|
|
3781
|
-
} catch (
|
|
4008
|
+
} catch (_error) {
|
|
3782
4009
|
throw new CodexError(
|
|
3783
4010
|
"CLI_NOT_FOUND" /* CLI_NOT_FOUND */,
|
|
3784
4011
|
"Codex SDK not available. Install with: npm install @openai/codex-sdk"
|
|
@@ -4129,7 +4356,7 @@ async function isCLIModeAvailable() {
|
|
|
4129
4356
|
execSync("codex --version", {
|
|
4130
4357
|
encoding: "utf-8",
|
|
4131
4358
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4132
|
-
timeout:
|
|
4359
|
+
timeout: TIMEOUTS.QUICK_COMMAND
|
|
4133
4360
|
});
|
|
4134
4361
|
return true;
|
|
4135
4362
|
} catch {
|
|
@@ -4141,6 +4368,3205 @@ var init_openai_provider_factory = __esm({
|
|
|
4141
4368
|
init_esm_shims();
|
|
4142
4369
|
init_openai_provider();
|
|
4143
4370
|
init_logger();
|
|
4371
|
+
init_validation_limits();
|
|
4372
|
+
}
|
|
4373
|
+
});
|
|
4374
|
+
var FeatureFlagManager;
|
|
4375
|
+
var init_flag_manager = __esm({
|
|
4376
|
+
"src/core/feature-flags/flag-manager.ts"() {
|
|
4377
|
+
init_esm_shims();
|
|
4378
|
+
init_logger();
|
|
4379
|
+
FeatureFlagManager = class {
|
|
4380
|
+
flags = /* @__PURE__ */ new Map();
|
|
4381
|
+
storagePath;
|
|
4382
|
+
flagsDir;
|
|
4383
|
+
workspacePath;
|
|
4384
|
+
constructor(workspacePath = process.cwd()) {
|
|
4385
|
+
this.workspacePath = workspacePath;
|
|
4386
|
+
this.flagsDir = join(workspacePath, ".automatosx");
|
|
4387
|
+
this.storagePath = join(this.flagsDir, "feature-flags.json");
|
|
4388
|
+
this.loadFlags();
|
|
4389
|
+
}
|
|
4390
|
+
/**
|
|
4391
|
+
* Check if workspace has a storage directory
|
|
4392
|
+
*/
|
|
4393
|
+
hasStorage() {
|
|
4394
|
+
return existsSync(this.flagsDir);
|
|
4395
|
+
}
|
|
4396
|
+
/**
|
|
4397
|
+
* Get workspace path for diagnostics
|
|
4398
|
+
*/
|
|
4399
|
+
getWorkspacePath() {
|
|
4400
|
+
return this.workspacePath;
|
|
4401
|
+
}
|
|
4402
|
+
/**
|
|
4403
|
+
* Load flags from storage
|
|
4404
|
+
*/
|
|
4405
|
+
loadFlags() {
|
|
4406
|
+
try {
|
|
4407
|
+
if (!this.hasStorage()) {
|
|
4408
|
+
logger.debug("Feature flag storage not initialized", {
|
|
4409
|
+
workspacePath: this.workspacePath
|
|
4410
|
+
});
|
|
4411
|
+
return;
|
|
4412
|
+
}
|
|
4413
|
+
if (existsSync(this.storagePath)) {
|
|
4414
|
+
const data = readFileSync(this.storagePath, "utf-8");
|
|
4415
|
+
const flags = JSON.parse(data);
|
|
4416
|
+
for (const flag of flags) {
|
|
4417
|
+
this.flags.set(flag.name, flag);
|
|
4418
|
+
}
|
|
4419
|
+
logger.info("Feature flags loaded", {
|
|
4420
|
+
count: flags.length,
|
|
4421
|
+
path: this.storagePath
|
|
4422
|
+
});
|
|
4423
|
+
} else {
|
|
4424
|
+
logger.info("No feature flags file found, starting fresh");
|
|
4425
|
+
}
|
|
4426
|
+
} catch (error) {
|
|
4427
|
+
logger.error("Failed to load feature flags", {
|
|
4428
|
+
error: error.message
|
|
4429
|
+
});
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
/**
|
|
4433
|
+
* Save flags to storage
|
|
4434
|
+
*/
|
|
4435
|
+
saveFlags() {
|
|
4436
|
+
try {
|
|
4437
|
+
this.ensureStorageDir();
|
|
4438
|
+
const flagsArray = Array.from(this.flags.values());
|
|
4439
|
+
const data = JSON.stringify(flagsArray, null, 2);
|
|
4440
|
+
writeFileSync(this.storagePath, data, "utf-8");
|
|
4441
|
+
} catch (error) {
|
|
4442
|
+
logger.error("Failed to save feature flags", {
|
|
4443
|
+
error: error.message
|
|
4444
|
+
});
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
/**
|
|
4448
|
+
* Ensure storage directory exists before writing
|
|
4449
|
+
*/
|
|
4450
|
+
ensureStorageDir() {
|
|
4451
|
+
if (!existsSync(this.flagsDir)) {
|
|
4452
|
+
mkdirSync(this.flagsDir, { recursive: true });
|
|
4453
|
+
}
|
|
4454
|
+
}
|
|
4455
|
+
/**
|
|
4456
|
+
* Define a feature flag
|
|
4457
|
+
*/
|
|
4458
|
+
defineFlag(flag) {
|
|
4459
|
+
this.flags.set(flag.name, flag);
|
|
4460
|
+
this.saveFlags();
|
|
4461
|
+
logger.info("Feature flag defined", { name: flag.name });
|
|
4462
|
+
}
|
|
4463
|
+
/**
|
|
4464
|
+
* Check if flag is enabled
|
|
4465
|
+
*/
|
|
4466
|
+
isEnabled(flagName, context) {
|
|
4467
|
+
const flag = this.flags.get(flagName);
|
|
4468
|
+
if (!flag) return false;
|
|
4469
|
+
if (flag.killSwitch?.enabled) {
|
|
4470
|
+
logger.warn(`Feature flag ${flagName} killed`, {
|
|
4471
|
+
reason: flag.killSwitch.reason,
|
|
4472
|
+
timestamp: flag.killSwitch.timestamp
|
|
4473
|
+
});
|
|
4474
|
+
return false;
|
|
4475
|
+
}
|
|
4476
|
+
const envOverride = process.env[`FEATURE_${flagName.toUpperCase()}`];
|
|
4477
|
+
if (envOverride === "false" || envOverride === "0") {
|
|
4478
|
+
logger.warn(`Feature flag ${flagName} disabled by env var`);
|
|
4479
|
+
return false;
|
|
4480
|
+
}
|
|
4481
|
+
if (flag.rolloutPercentage !== void 0) {
|
|
4482
|
+
const hash = this.hashContext(context);
|
|
4483
|
+
const bucket = hash % 100;
|
|
4484
|
+
const enabled = bucket < flag.rolloutPercentage;
|
|
4485
|
+
return enabled;
|
|
4486
|
+
}
|
|
4487
|
+
return flag.enabled;
|
|
4488
|
+
}
|
|
4489
|
+
/**
|
|
4490
|
+
* Gradually increase rollout percentage
|
|
4491
|
+
*/
|
|
4492
|
+
async increaseRollout(flagName, newPercentage, options) {
|
|
4493
|
+
const flag = this.flags.get(flagName);
|
|
4494
|
+
if (!flag) throw new Error(`Flag ${flagName} not found`);
|
|
4495
|
+
const currentPercentage = flag.rolloutPercentage || 0;
|
|
4496
|
+
if (newPercentage > currentPercentage * 5 && newPercentage !== 100 && currentPercentage > 0) {
|
|
4497
|
+
throw new Error(
|
|
4498
|
+
`Rollout increase too aggressive: ${currentPercentage}% \u2192 ${newPercentage}%
|
|
4499
|
+
Maximum safe increase: ${currentPercentage * 5}%`
|
|
4500
|
+
);
|
|
4501
|
+
}
|
|
4502
|
+
if (options?.minimumDuration && flag.lastUpdated) {
|
|
4503
|
+
const timeSinceLastChange = Date.now() - flag.lastUpdated;
|
|
4504
|
+
if (timeSinceLastChange < options.minimumDuration) {
|
|
4505
|
+
throw new Error(
|
|
4506
|
+
`Must wait ${options.minimumDuration}ms before increasing rollout
|
|
4507
|
+
Time since last change: ${timeSinceLastChange}ms`
|
|
4508
|
+
);
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
flag.rolloutPercentage = newPercentage;
|
|
4512
|
+
flag.lastUpdated = Date.now();
|
|
4513
|
+
this.flags.set(flagName, flag);
|
|
4514
|
+
this.saveFlags();
|
|
4515
|
+
logger.info(`Feature flag ${flagName} rollout increased`, {
|
|
4516
|
+
from: currentPercentage,
|
|
4517
|
+
to: newPercentage,
|
|
4518
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4519
|
+
});
|
|
4520
|
+
}
|
|
4521
|
+
/**
|
|
4522
|
+
* Emergency kill switch
|
|
4523
|
+
*/
|
|
4524
|
+
async killSwitch(flagName, reason) {
|
|
4525
|
+
const flag = this.flags.get(flagName);
|
|
4526
|
+
if (!flag) throw new Error(`Flag ${flagName} not found`);
|
|
4527
|
+
flag.killSwitch = {
|
|
4528
|
+
enabled: true,
|
|
4529
|
+
reason,
|
|
4530
|
+
timestamp: Date.now(),
|
|
4531
|
+
by: process.env.USER || "unknown"
|
|
4532
|
+
};
|
|
4533
|
+
this.flags.set(flagName, flag);
|
|
4534
|
+
this.saveFlags();
|
|
4535
|
+
logger.error(`\u{1F6A8} KILL SWITCH ACTIVATED: ${flagName}`, {
|
|
4536
|
+
reason,
|
|
4537
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4538
|
+
});
|
|
4539
|
+
}
|
|
4540
|
+
/**
|
|
4541
|
+
* Get all flags
|
|
4542
|
+
*/
|
|
4543
|
+
getAllFlags() {
|
|
4544
|
+
return Array.from(this.flags.values());
|
|
4545
|
+
}
|
|
4546
|
+
/**
|
|
4547
|
+
* Get a specific flag
|
|
4548
|
+
*/
|
|
4549
|
+
getFlag(flagName) {
|
|
4550
|
+
return this.flags.get(flagName);
|
|
4551
|
+
}
|
|
4552
|
+
/**
|
|
4553
|
+
* Hash context for deterministic bucketing
|
|
4554
|
+
*/
|
|
4555
|
+
hashContext(context) {
|
|
4556
|
+
const str = JSON.stringify(context || {});
|
|
4557
|
+
let hash = 0;
|
|
4558
|
+
for (let i = 0; i < str.length; i++) {
|
|
4559
|
+
const char = str.charCodeAt(i);
|
|
4560
|
+
hash = (hash << 5) - hash + char;
|
|
4561
|
+
hash = hash & hash;
|
|
4562
|
+
}
|
|
4563
|
+
return hash >>> 0;
|
|
4564
|
+
}
|
|
4565
|
+
};
|
|
4566
|
+
}
|
|
4567
|
+
});
|
|
4568
|
+
|
|
4569
|
+
// src/core/feature-flags/flags.ts
|
|
4570
|
+
function getFlagManager(workspacePath) {
|
|
4571
|
+
const path7 = process.cwd();
|
|
4572
|
+
if (!flagManagerInstances.has(path7)) {
|
|
4573
|
+
const manager = new FeatureFlagManager(path7);
|
|
4574
|
+
initializeFlags(manager);
|
|
4575
|
+
flagManagerInstances.set(path7, manager);
|
|
4576
|
+
}
|
|
4577
|
+
return flagManagerInstances.get(path7);
|
|
4578
|
+
}
|
|
4579
|
+
function initializeFlags(manager) {
|
|
4580
|
+
if (!manager.hasStorage()) {
|
|
4581
|
+
logger.debug("Skipping feature flag initialization; workspace not initialized", {
|
|
4582
|
+
workspacePath: manager.getWorkspacePath()
|
|
4583
|
+
});
|
|
4584
|
+
return;
|
|
4585
|
+
}
|
|
4586
|
+
initializeGeminiStreamingFlag(manager);
|
|
4587
|
+
initializeProviderArchitectureFlags(manager);
|
|
4588
|
+
}
|
|
4589
|
+
function initializeGeminiStreamingFlag(manager) {
|
|
4590
|
+
const existingFlag = manager.getFlag("gemini_streaming");
|
|
4591
|
+
if (!existingFlag) {
|
|
4592
|
+
manager.defineFlag({
|
|
4593
|
+
name: "gemini_streaming",
|
|
4594
|
+
description: "Enable Gemini as a valid option for streaming workloads",
|
|
4595
|
+
enabled: true,
|
|
4596
|
+
rolloutPercentage: 0,
|
|
4597
|
+
metadata: {
|
|
4598
|
+
owner: "platform-team",
|
|
4599
|
+
jiraTicket: "AUTO-1234",
|
|
4600
|
+
expectedImpact: "96% cost reduction on streaming tasks"
|
|
4601
|
+
}
|
|
4602
|
+
});
|
|
4603
|
+
logger.info("Feature flag gemini_streaming initialized at 0%");
|
|
4604
|
+
}
|
|
4605
|
+
}
|
|
4606
|
+
function initializeProviderArchitectureFlags(manager) {
|
|
4607
|
+
for (const flagDef of DEFAULT_PROVIDER_FLAGS) {
|
|
4608
|
+
const existingFlag = manager.getFlag(flagDef.name);
|
|
4609
|
+
if (!existingFlag) {
|
|
4610
|
+
manager.defineFlag(flagDef);
|
|
4611
|
+
logger.info(`Feature flag ${flagDef.name} initialized`, {
|
|
4612
|
+
enabled: flagDef.enabled,
|
|
4613
|
+
rolloutPercentage: flagDef.rolloutPercentage
|
|
4614
|
+
});
|
|
4615
|
+
}
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4618
|
+
function isSDKFirstModeEnabled(context) {
|
|
4619
|
+
const manager = getFlagManager();
|
|
4620
|
+
return manager.isEnabled(PROVIDER_ARCHITECTURE_FLAGS.SDK_FIRST_MODE, context);
|
|
4621
|
+
}
|
|
4622
|
+
function isSDKFallbackEnabled() {
|
|
4623
|
+
const manager = getFlagManager();
|
|
4624
|
+
return manager.isEnabled(PROVIDER_ARCHITECTURE_FLAGS.SDK_FALLBACK_ENABLED);
|
|
4625
|
+
}
|
|
4626
|
+
function shouldCollectProviderMetrics() {
|
|
4627
|
+
const manager = getFlagManager();
|
|
4628
|
+
return manager.isEnabled(PROVIDER_ARCHITECTURE_FLAGS.PROVIDER_METRICS);
|
|
4629
|
+
}
|
|
4630
|
+
var flagManagerInstances, PROVIDER_ARCHITECTURE_FLAGS, DEFAULT_PROVIDER_FLAGS;
|
|
4631
|
+
var init_flags = __esm({
|
|
4632
|
+
"src/core/feature-flags/flags.ts"() {
|
|
4633
|
+
init_esm_shims();
|
|
4634
|
+
init_flag_manager();
|
|
4635
|
+
init_logger();
|
|
4636
|
+
flagManagerInstances = /* @__PURE__ */ new Map();
|
|
4637
|
+
PROVIDER_ARCHITECTURE_FLAGS = {
|
|
4638
|
+
/** Enable SDK-first execution pattern (SDK primary, CLI fallback) */
|
|
4639
|
+
SDK_FIRST_MODE: "sdk_first_mode",
|
|
4640
|
+
/** Enable MCP bidirectional communication for agentic providers */
|
|
4641
|
+
MCP_BIDIRECTIONAL: "mcp_bidirectional",
|
|
4642
|
+
/** Auto-inject MCP server config into provider config files */
|
|
4643
|
+
AUTO_INJECT_MCP_CONFIG: "auto_inject_mcp_config",
|
|
4644
|
+
/** Allow CLI fallback when SDK fails */
|
|
4645
|
+
SDK_FALLBACK_ENABLED: "sdk_fallback_enabled",
|
|
4646
|
+
/** Show deprecation warnings (legacy providers, etc.) */
|
|
4647
|
+
DEPRECATION_WARNINGS: "deprecation_warnings",
|
|
4648
|
+
/** Enable provider metrics collection */
|
|
4649
|
+
PROVIDER_METRICS: "provider_metrics"
|
|
4650
|
+
};
|
|
4651
|
+
DEFAULT_PROVIDER_FLAGS = [
|
|
4652
|
+
{
|
|
4653
|
+
name: PROVIDER_ARCHITECTURE_FLAGS.SDK_FIRST_MODE,
|
|
4654
|
+
description: "Enable SDK-first execution pattern (SDK primary, CLI fallback) for GLM, Grok, and Codex providers",
|
|
4655
|
+
enabled: false,
|
|
4656
|
+
// Disabled by default, enable in v11.7.0
|
|
4657
|
+
rolloutPercentage: 0,
|
|
4658
|
+
metadata: {
|
|
4659
|
+
owner: "platform-team",
|
|
4660
|
+
expectedImpact: "Lower latency (~5ms vs ~200ms), better type safety"
|
|
4661
|
+
}
|
|
4662
|
+
},
|
|
4663
|
+
{
|
|
4664
|
+
name: PROVIDER_ARCHITECTURE_FLAGS.MCP_BIDIRECTIONAL,
|
|
4665
|
+
description: "Enable MCP bidirectional communication for Claude and Gemini agents",
|
|
4666
|
+
enabled: false,
|
|
4667
|
+
// Disabled by default, enable in v11.8.0
|
|
4668
|
+
rolloutPercentage: 0,
|
|
4669
|
+
metadata: {
|
|
4670
|
+
owner: "platform-team",
|
|
4671
|
+
expectedImpact: "Faster agent communication via MCP instead of stdout parsing"
|
|
4672
|
+
}
|
|
4673
|
+
},
|
|
4674
|
+
{
|
|
4675
|
+
name: PROVIDER_ARCHITECTURE_FLAGS.AUTO_INJECT_MCP_CONFIG,
|
|
4676
|
+
description: "Automatically inject AutomatosX MCP server config into provider config files",
|
|
4677
|
+
enabled: false,
|
|
4678
|
+
// Disabled by default, requires careful testing
|
|
4679
|
+
rolloutPercentage: 0,
|
|
4680
|
+
metadata: {
|
|
4681
|
+
owner: "platform-team",
|
|
4682
|
+
expectedImpact: "Seamless MCP setup for agents"
|
|
4683
|
+
}
|
|
4684
|
+
},
|
|
4685
|
+
{
|
|
4686
|
+
name: PROVIDER_ARCHITECTURE_FLAGS.SDK_FALLBACK_ENABLED,
|
|
4687
|
+
description: "Allow CLI fallback when SDK execution fails",
|
|
4688
|
+
enabled: true,
|
|
4689
|
+
// Enabled by default for safety
|
|
4690
|
+
rolloutPercentage: 100,
|
|
4691
|
+
metadata: {
|
|
4692
|
+
owner: "platform-team",
|
|
4693
|
+
expectedImpact: "Graceful degradation when SDK unavailable"
|
|
4694
|
+
}
|
|
4695
|
+
},
|
|
4696
|
+
{
|
|
4697
|
+
name: PROVIDER_ARCHITECTURE_FLAGS.DEPRECATION_WARNINGS,
|
|
4698
|
+
description: "Show deprecation warnings for legacy features",
|
|
4699
|
+
enabled: true,
|
|
4700
|
+
// Enabled by default to warn users
|
|
4701
|
+
rolloutPercentage: 100,
|
|
4702
|
+
metadata: {
|
|
4703
|
+
owner: "platform-team",
|
|
4704
|
+
expectedImpact: "v13.0.0: ax-cli removed, GLM/Grok are SDK-first"
|
|
4705
|
+
}
|
|
4706
|
+
},
|
|
4707
|
+
{
|
|
4708
|
+
name: PROVIDER_ARCHITECTURE_FLAGS.PROVIDER_METRICS,
|
|
4709
|
+
description: "Enable provider metrics collection (SDK/CLI usage, fallback rate, MCP connections)",
|
|
4710
|
+
enabled: true,
|
|
4711
|
+
// Enabled by default for observability
|
|
4712
|
+
rolloutPercentage: 100,
|
|
4713
|
+
metadata: {
|
|
4714
|
+
owner: "platform-team",
|
|
4715
|
+
expectedImpact: "Better visibility into provider performance"
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
];
|
|
4719
|
+
}
|
|
4720
|
+
});
|
|
4721
|
+
|
|
4722
|
+
// src/providers/fallback-decision.ts
|
|
4723
|
+
function decideFallback(error, providerName) {
|
|
4724
|
+
const errorMessage = getErrorMessage(error).toLowerCase();
|
|
4725
|
+
const errorCode = getErrorCode(error);
|
|
4726
|
+
const statusCode = getStatusCode(error);
|
|
4727
|
+
logger.debug("Classifying error for fallback decision", {
|
|
4728
|
+
provider: providerName,
|
|
4729
|
+
message: errorMessage.substring(0, 100),
|
|
4730
|
+
code: errorCode,
|
|
4731
|
+
statusCode
|
|
4732
|
+
});
|
|
4733
|
+
if (matchesPatterns(errorMessage, SDK_UNAVAILABLE_PATTERNS)) {
|
|
4734
|
+
return {
|
|
4735
|
+
decision: "use_cli" /* USE_CLI */,
|
|
4736
|
+
reason: "SDK not available or not installed",
|
|
4737
|
+
severity: "warn"
|
|
4738
|
+
};
|
|
4739
|
+
}
|
|
4740
|
+
if (matchesPatterns(errorMessage, AUTH_ERROR_PATTERNS) || statusCode === 401 || statusCode === 403) {
|
|
4741
|
+
return {
|
|
4742
|
+
decision: "propagate" /* PROPAGATE */,
|
|
4743
|
+
reason: "Authentication error - CLI would fail with same credentials",
|
|
4744
|
+
severity: "error"
|
|
4745
|
+
};
|
|
4746
|
+
}
|
|
4747
|
+
if (matchesPatterns(errorMessage, RATE_LIMIT_PATTERNS) || statusCode === 429) {
|
|
4748
|
+
return {
|
|
4749
|
+
decision: "retry_sdk" /* RETRY_SDK */,
|
|
4750
|
+
reason: "Rate limited - retrying with backoff",
|
|
4751
|
+
severity: "warn",
|
|
4752
|
+
retryDelayMs: 5e3
|
|
4753
|
+
// Longer delay for rate limits
|
|
4754
|
+
};
|
|
4755
|
+
}
|
|
4756
|
+
if (matchesPatterns(errorMessage, TRANSIENT_ERROR_PATTERNS)) {
|
|
4757
|
+
return {
|
|
4758
|
+
decision: "retry_sdk" /* RETRY_SDK */,
|
|
4759
|
+
reason: "Transient network error - retrying",
|
|
4760
|
+
severity: "warn",
|
|
4761
|
+
retryDelayMs: 1e3
|
|
4762
|
+
};
|
|
4763
|
+
}
|
|
4764
|
+
if (matchesPatterns(errorMessage, SERVER_ERROR_PATTERNS) || statusCode && statusCode >= 500 && statusCode < 600) {
|
|
4765
|
+
return {
|
|
4766
|
+
decision: "retry_sdk" /* RETRY_SDK */,
|
|
4767
|
+
reason: "Server error - retrying",
|
|
4768
|
+
severity: "warn",
|
|
4769
|
+
retryDelayMs: 2e3
|
|
4770
|
+
};
|
|
4771
|
+
}
|
|
4772
|
+
if (matchesPatterns(errorMessage, CLIENT_ERROR_PATTERNS) || statusCode && statusCode >= 400 && statusCode < 500) {
|
|
4773
|
+
return {
|
|
4774
|
+
decision: "propagate" /* PROPAGATE */,
|
|
4775
|
+
reason: "Client error - request is invalid",
|
|
4776
|
+
severity: "error"
|
|
4777
|
+
};
|
|
4778
|
+
}
|
|
4779
|
+
if (isSDKFallbackEnabled()) {
|
|
4780
|
+
return {
|
|
4781
|
+
decision: "use_cli" /* USE_CLI */,
|
|
4782
|
+
reason: "Unknown error - falling back to CLI",
|
|
4783
|
+
severity: "warn"
|
|
4784
|
+
};
|
|
4785
|
+
}
|
|
4786
|
+
return {
|
|
4787
|
+
decision: "propagate" /* PROPAGATE */,
|
|
4788
|
+
reason: "Unknown error and fallback is disabled",
|
|
4789
|
+
severity: "error"
|
|
4790
|
+
};
|
|
4791
|
+
}
|
|
4792
|
+
function getErrorMessage(error) {
|
|
4793
|
+
if (error instanceof Error) {
|
|
4794
|
+
return error.message;
|
|
4795
|
+
}
|
|
4796
|
+
if (typeof error === "string") {
|
|
4797
|
+
return error;
|
|
4798
|
+
}
|
|
4799
|
+
if (error && typeof error === "object") {
|
|
4800
|
+
const obj = error;
|
|
4801
|
+
if (typeof obj.message === "string") {
|
|
4802
|
+
return obj.message;
|
|
4803
|
+
}
|
|
4804
|
+
if (typeof obj.error === "string") {
|
|
4805
|
+
return obj.error;
|
|
4806
|
+
}
|
|
4807
|
+
if (obj.error && typeof obj.error === "object") {
|
|
4808
|
+
const innerError = obj.error;
|
|
4809
|
+
if (typeof innerError.message === "string") {
|
|
4810
|
+
return innerError.message;
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4813
|
+
}
|
|
4814
|
+
return String(error);
|
|
4815
|
+
}
|
|
4816
|
+
function getErrorCode(error) {
|
|
4817
|
+
if (error && typeof error === "object") {
|
|
4818
|
+
const obj = error;
|
|
4819
|
+
if (typeof obj.code === "string") {
|
|
4820
|
+
return obj.code;
|
|
4821
|
+
}
|
|
4822
|
+
if (typeof obj.errorCode === "string") {
|
|
4823
|
+
return obj.errorCode;
|
|
4824
|
+
}
|
|
4825
|
+
}
|
|
4826
|
+
return void 0;
|
|
4827
|
+
}
|
|
4828
|
+
function getStatusCode(error) {
|
|
4829
|
+
if (error && typeof error === "object") {
|
|
4830
|
+
const obj = error;
|
|
4831
|
+
if (typeof obj.status === "number") {
|
|
4832
|
+
return obj.status;
|
|
4833
|
+
}
|
|
4834
|
+
if (typeof obj.statusCode === "number") {
|
|
4835
|
+
return obj.statusCode;
|
|
4836
|
+
}
|
|
4837
|
+
if (obj.response && typeof obj.response === "object") {
|
|
4838
|
+
const response = obj.response;
|
|
4839
|
+
if (typeof response.status === "number") {
|
|
4840
|
+
return response.status;
|
|
4841
|
+
}
|
|
4842
|
+
}
|
|
4843
|
+
}
|
|
4844
|
+
return void 0;
|
|
4845
|
+
}
|
|
4846
|
+
function matchesPatterns(message, patterns) {
|
|
4847
|
+
return patterns.some((pattern) => message.includes(pattern.toLowerCase()));
|
|
4848
|
+
}
|
|
4849
|
+
var SDK_UNAVAILABLE_PATTERNS, AUTH_ERROR_PATTERNS, TRANSIENT_ERROR_PATTERNS, RATE_LIMIT_PATTERNS, SERVER_ERROR_PATTERNS, CLIENT_ERROR_PATTERNS;
|
|
4850
|
+
var init_fallback_decision = __esm({
|
|
4851
|
+
"src/providers/fallback-decision.ts"() {
|
|
4852
|
+
init_esm_shims();
|
|
4853
|
+
init_logger();
|
|
4854
|
+
init_flags();
|
|
4855
|
+
SDK_UNAVAILABLE_PATTERNS = [
|
|
4856
|
+
"module not found",
|
|
4857
|
+
"cannot find module",
|
|
4858
|
+
"sdk not available",
|
|
4859
|
+
"sdk not installed",
|
|
4860
|
+
"failed to initialize sdk",
|
|
4861
|
+
"sdk initialization failed",
|
|
4862
|
+
"package not found",
|
|
4863
|
+
"@zhipuai/sdk",
|
|
4864
|
+
"@xai-org/grok-sdk",
|
|
4865
|
+
"@openai/codex-sdk",
|
|
4866
|
+
"enoent",
|
|
4867
|
+
// File not found (npm module missing)
|
|
4868
|
+
"cannot resolve",
|
|
4869
|
+
"dynamic import failed"
|
|
4870
|
+
];
|
|
4871
|
+
AUTH_ERROR_PATTERNS = [
|
|
4872
|
+
"authentication failed",
|
|
4873
|
+
"invalid api key",
|
|
4874
|
+
"invalid_api_key",
|
|
4875
|
+
"unauthorized",
|
|
4876
|
+
"api key not found",
|
|
4877
|
+
"api_key_invalid",
|
|
4878
|
+
"invalid credentials",
|
|
4879
|
+
"authentication required",
|
|
4880
|
+
"access denied",
|
|
4881
|
+
"401",
|
|
4882
|
+
"403",
|
|
4883
|
+
"forbidden",
|
|
4884
|
+
"invalid_grant",
|
|
4885
|
+
"token expired",
|
|
4886
|
+
"invalid token"
|
|
4887
|
+
];
|
|
4888
|
+
TRANSIENT_ERROR_PATTERNS = [
|
|
4889
|
+
"timeout",
|
|
4890
|
+
"etimedout",
|
|
4891
|
+
"econnreset",
|
|
4892
|
+
"econnrefused",
|
|
4893
|
+
"enotfound",
|
|
4894
|
+
"network error",
|
|
4895
|
+
"socket hang up",
|
|
4896
|
+
"connection reset",
|
|
4897
|
+
"temporary failure",
|
|
4898
|
+
"service unavailable",
|
|
4899
|
+
"503",
|
|
4900
|
+
"504",
|
|
4901
|
+
"gateway timeout",
|
|
4902
|
+
"bad gateway",
|
|
4903
|
+
"502"
|
|
4904
|
+
];
|
|
4905
|
+
RATE_LIMIT_PATTERNS = [
|
|
4906
|
+
"rate limit",
|
|
4907
|
+
"rate_limit",
|
|
4908
|
+
"too many requests",
|
|
4909
|
+
"429",
|
|
4910
|
+
"throttled",
|
|
4911
|
+
"quota exceeded",
|
|
4912
|
+
"resource_exhausted",
|
|
4913
|
+
"overloaded"
|
|
4914
|
+
];
|
|
4915
|
+
SERVER_ERROR_PATTERNS = [
|
|
4916
|
+
"500",
|
|
4917
|
+
"502",
|
|
4918
|
+
"503",
|
|
4919
|
+
"504",
|
|
4920
|
+
"internal server error",
|
|
4921
|
+
"internal error",
|
|
4922
|
+
"server error"
|
|
4923
|
+
];
|
|
4924
|
+
CLIENT_ERROR_PATTERNS = [
|
|
4925
|
+
"400",
|
|
4926
|
+
"bad request",
|
|
4927
|
+
"invalid request",
|
|
4928
|
+
"validation error",
|
|
4929
|
+
"invalid parameter",
|
|
4930
|
+
"missing parameter",
|
|
4931
|
+
"malformed request"
|
|
4932
|
+
];
|
|
4933
|
+
}
|
|
4934
|
+
});
|
|
4935
|
+
|
|
4936
|
+
// src/core/metrics/provider-metrics.ts
|
|
4937
|
+
function getProviderMetrics() {
|
|
4938
|
+
return ProviderMetricsCollector.getInstance();
|
|
4939
|
+
}
|
|
4940
|
+
var ProviderMetricsCollector;
|
|
4941
|
+
var init_provider_metrics = __esm({
|
|
4942
|
+
"src/core/metrics/provider-metrics.ts"() {
|
|
4943
|
+
init_esm_shims();
|
|
4944
|
+
init_logger();
|
|
4945
|
+
init_flags();
|
|
4946
|
+
ProviderMetricsCollector = class _ProviderMetricsCollector {
|
|
4947
|
+
static instance = null;
|
|
4948
|
+
// Per-provider metrics
|
|
4949
|
+
providerMetrics = /* @__PURE__ */ new Map();
|
|
4950
|
+
// MCP metrics
|
|
4951
|
+
mcpMetrics = {
|
|
4952
|
+
totalConnections: 0,
|
|
4953
|
+
activeConnections: 0,
|
|
4954
|
+
failedConnections: 0,
|
|
4955
|
+
totalToolCalls: 0,
|
|
4956
|
+
avgToolCallLatencyMs: 0,
|
|
4957
|
+
sessionsByProvider: {}
|
|
4958
|
+
};
|
|
4959
|
+
// Latency tracking (for averages)
|
|
4960
|
+
sdkLatencies = /* @__PURE__ */ new Map();
|
|
4961
|
+
cliLatencies = /* @__PURE__ */ new Map();
|
|
4962
|
+
mcpLatencies = [];
|
|
4963
|
+
// Max samples to keep for latency averaging
|
|
4964
|
+
MAX_LATENCY_SAMPLES = 100;
|
|
4965
|
+
constructor() {
|
|
4966
|
+
}
|
|
4967
|
+
/**
|
|
4968
|
+
* Get singleton instance
|
|
4969
|
+
*/
|
|
4970
|
+
static getInstance() {
|
|
4971
|
+
if (!_ProviderMetricsCollector.instance) {
|
|
4972
|
+
_ProviderMetricsCollector.instance = new _ProviderMetricsCollector();
|
|
4973
|
+
}
|
|
4974
|
+
return _ProviderMetricsCollector.instance;
|
|
4975
|
+
}
|
|
4976
|
+
/**
|
|
4977
|
+
* Reset singleton instance (for testing)
|
|
4978
|
+
*/
|
|
4979
|
+
static resetInstance() {
|
|
4980
|
+
_ProviderMetricsCollector.instance = null;
|
|
4981
|
+
}
|
|
4982
|
+
/**
|
|
4983
|
+
* Check if metrics collection is enabled
|
|
4984
|
+
*/
|
|
4985
|
+
isEnabled() {
|
|
4986
|
+
return shouldCollectProviderMetrics();
|
|
4987
|
+
}
|
|
4988
|
+
/**
|
|
4989
|
+
* Get or create provider metrics data
|
|
4990
|
+
*/
|
|
4991
|
+
getOrCreateProviderMetrics(providerName) {
|
|
4992
|
+
if (!this.providerMetrics.has(providerName)) {
|
|
4993
|
+
this.providerMetrics.set(providerName, {
|
|
4994
|
+
providerName,
|
|
4995
|
+
executionMode: "cli",
|
|
4996
|
+
// Default mode
|
|
4997
|
+
circuitBreakerState: "closed",
|
|
4998
|
+
metrics: {
|
|
4999
|
+
sdkExecutions: 0,
|
|
5000
|
+
cliExecutions: 0,
|
|
5001
|
+
mcpToolCalls: 0,
|
|
5002
|
+
sdkFallbacks: 0,
|
|
5003
|
+
sdkErrors: 0,
|
|
5004
|
+
cliErrors: 0,
|
|
5005
|
+
mcpConnectionFailures: 0,
|
|
5006
|
+
avgSdkLatencyMs: 0,
|
|
5007
|
+
avgCliLatencyMs: 0,
|
|
5008
|
+
avgMcpLatencyMs: 0,
|
|
5009
|
+
fallbackRate: 0,
|
|
5010
|
+
sdkSuccessRate: 1,
|
|
5011
|
+
cliSuccessRate: 1,
|
|
5012
|
+
mcpSuccessRate: 1
|
|
5013
|
+
},
|
|
5014
|
+
lastUpdated: Date.now()
|
|
5015
|
+
});
|
|
5016
|
+
}
|
|
5017
|
+
return this.providerMetrics.get(providerName);
|
|
5018
|
+
}
|
|
5019
|
+
/**
|
|
5020
|
+
* Record SDK execution
|
|
5021
|
+
*/
|
|
5022
|
+
recordSDKExecution(providerName, latencyMs, success) {
|
|
5023
|
+
if (!this.isEnabled()) return;
|
|
5024
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5025
|
+
data.metrics.sdkExecutions++;
|
|
5026
|
+
if (!success) {
|
|
5027
|
+
data.metrics.sdkErrors++;
|
|
5028
|
+
}
|
|
5029
|
+
this.addLatencySample(this.sdkLatencies, providerName, latencyMs);
|
|
5030
|
+
data.metrics.avgSdkLatencyMs = this.calculateAverageLatency(
|
|
5031
|
+
this.sdkLatencies.get(providerName) || []
|
|
5032
|
+
);
|
|
5033
|
+
data.metrics.sdkSuccessRate = this.calculateSuccessRate(
|
|
5034
|
+
data.metrics.sdkExecutions,
|
|
5035
|
+
data.metrics.sdkErrors
|
|
5036
|
+
);
|
|
5037
|
+
data.lastUpdated = Date.now();
|
|
5038
|
+
logger.debug("SDK execution recorded", {
|
|
5039
|
+
provider: providerName,
|
|
5040
|
+
latencyMs,
|
|
5041
|
+
success,
|
|
5042
|
+
totalExecutions: data.metrics.sdkExecutions
|
|
5043
|
+
});
|
|
5044
|
+
}
|
|
5045
|
+
/**
|
|
5046
|
+
* Record CLI execution
|
|
5047
|
+
*/
|
|
5048
|
+
recordCLIExecution(providerName, latencyMs, success) {
|
|
5049
|
+
if (!this.isEnabled()) return;
|
|
5050
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5051
|
+
data.metrics.cliExecutions++;
|
|
5052
|
+
if (!success) {
|
|
5053
|
+
data.metrics.cliErrors++;
|
|
5054
|
+
}
|
|
5055
|
+
this.addLatencySample(this.cliLatencies, providerName, latencyMs);
|
|
5056
|
+
data.metrics.avgCliLatencyMs = this.calculateAverageLatency(
|
|
5057
|
+
this.cliLatencies.get(providerName) || []
|
|
5058
|
+
);
|
|
5059
|
+
data.metrics.cliSuccessRate = this.calculateSuccessRate(
|
|
5060
|
+
data.metrics.cliExecutions,
|
|
5061
|
+
data.metrics.cliErrors
|
|
5062
|
+
);
|
|
5063
|
+
data.lastUpdated = Date.now();
|
|
5064
|
+
logger.debug("CLI execution recorded", {
|
|
5065
|
+
provider: providerName,
|
|
5066
|
+
latencyMs,
|
|
5067
|
+
success,
|
|
5068
|
+
totalExecutions: data.metrics.cliExecutions
|
|
5069
|
+
});
|
|
5070
|
+
}
|
|
5071
|
+
/**
|
|
5072
|
+
* Record SDK fallback to CLI
|
|
5073
|
+
*/
|
|
5074
|
+
recordSDKFallback(providerName, reason) {
|
|
5075
|
+
if (!this.isEnabled()) return;
|
|
5076
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5077
|
+
data.metrics.sdkFallbacks++;
|
|
5078
|
+
if (data.metrics.sdkExecutions > 0) {
|
|
5079
|
+
data.metrics.fallbackRate = data.metrics.sdkFallbacks / data.metrics.sdkExecutions;
|
|
5080
|
+
}
|
|
5081
|
+
data.lastUpdated = Date.now();
|
|
5082
|
+
logger.info("SDK fallback recorded", {
|
|
5083
|
+
provider: providerName,
|
|
5084
|
+
reason,
|
|
5085
|
+
fallbackRate: data.metrics.fallbackRate
|
|
5086
|
+
});
|
|
5087
|
+
}
|
|
5088
|
+
/**
|
|
5089
|
+
* Record MCP connection
|
|
5090
|
+
*/
|
|
5091
|
+
recordMCPConnection(providerName, success) {
|
|
5092
|
+
if (!this.isEnabled()) return;
|
|
5093
|
+
this.mcpMetrics.totalConnections++;
|
|
5094
|
+
if (success) {
|
|
5095
|
+
this.mcpMetrics.activeConnections++;
|
|
5096
|
+
this.mcpMetrics.sessionsByProvider[providerName] = (this.mcpMetrics.sessionsByProvider[providerName] || 0) + 1;
|
|
5097
|
+
} else {
|
|
5098
|
+
this.mcpMetrics.failedConnections++;
|
|
5099
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5100
|
+
data.metrics.mcpConnectionFailures++;
|
|
5101
|
+
data.lastUpdated = Date.now();
|
|
5102
|
+
}
|
|
5103
|
+
logger.debug("MCP connection recorded", {
|
|
5104
|
+
provider: providerName,
|
|
5105
|
+
success,
|
|
5106
|
+
activeConnections: this.mcpMetrics.activeConnections
|
|
5107
|
+
});
|
|
5108
|
+
}
|
|
5109
|
+
/**
|
|
5110
|
+
* Record MCP connection closed
|
|
5111
|
+
*/
|
|
5112
|
+
recordMCPDisconnection(providerName) {
|
|
5113
|
+
if (!this.isEnabled()) return;
|
|
5114
|
+
if (this.mcpMetrics.activeConnections > 0) {
|
|
5115
|
+
this.mcpMetrics.activeConnections--;
|
|
5116
|
+
}
|
|
5117
|
+
const currentCount = this.mcpMetrics.sessionsByProvider[providerName];
|
|
5118
|
+
if (currentCount !== void 0 && currentCount > 0) {
|
|
5119
|
+
this.mcpMetrics.sessionsByProvider[providerName] = currentCount - 1;
|
|
5120
|
+
}
|
|
5121
|
+
logger.debug("MCP disconnection recorded", {
|
|
5122
|
+
provider: providerName,
|
|
5123
|
+
activeConnections: this.mcpMetrics.activeConnections
|
|
5124
|
+
});
|
|
5125
|
+
}
|
|
5126
|
+
/**
|
|
5127
|
+
* Record MCP tool call
|
|
5128
|
+
*/
|
|
5129
|
+
recordMCPToolCall(providerName, toolName, latencyMs, success) {
|
|
5130
|
+
if (!this.isEnabled()) return;
|
|
5131
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5132
|
+
data.metrics.mcpToolCalls++;
|
|
5133
|
+
this.mcpMetrics.totalToolCalls++;
|
|
5134
|
+
this.mcpLatencies.push(latencyMs);
|
|
5135
|
+
if (this.mcpLatencies.length > this.MAX_LATENCY_SAMPLES) {
|
|
5136
|
+
this.mcpLatencies.shift();
|
|
5137
|
+
}
|
|
5138
|
+
data.metrics.avgMcpLatencyMs = this.calculateAverageLatency(this.mcpLatencies);
|
|
5139
|
+
this.mcpMetrics.avgToolCallLatencyMs = data.metrics.avgMcpLatencyMs;
|
|
5140
|
+
data.lastUpdated = Date.now();
|
|
5141
|
+
logger.debug("MCP tool call recorded", {
|
|
5142
|
+
provider: providerName,
|
|
5143
|
+
tool: toolName,
|
|
5144
|
+
latencyMs,
|
|
5145
|
+
success
|
|
5146
|
+
});
|
|
5147
|
+
}
|
|
5148
|
+
/**
|
|
5149
|
+
* Update circuit breaker state
|
|
5150
|
+
*/
|
|
5151
|
+
updateCircuitBreakerState(providerName, state) {
|
|
5152
|
+
if (!this.isEnabled()) return;
|
|
5153
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5154
|
+
const previousState = data.circuitBreakerState;
|
|
5155
|
+
data.circuitBreakerState = state;
|
|
5156
|
+
data.lastUpdated = Date.now();
|
|
5157
|
+
if (previousState !== state) {
|
|
5158
|
+
logger.info("Circuit breaker state changed", {
|
|
5159
|
+
provider: providerName,
|
|
5160
|
+
from: previousState,
|
|
5161
|
+
to: state
|
|
5162
|
+
});
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
/**
|
|
5166
|
+
* Update execution mode
|
|
5167
|
+
*/
|
|
5168
|
+
updateExecutionMode(providerName, mode) {
|
|
5169
|
+
if (!this.isEnabled()) return;
|
|
5170
|
+
const data = this.getOrCreateProviderMetrics(providerName);
|
|
5171
|
+
data.executionMode = mode;
|
|
5172
|
+
data.lastUpdated = Date.now();
|
|
5173
|
+
}
|
|
5174
|
+
/**
|
|
5175
|
+
* Get metrics for a specific provider
|
|
5176
|
+
*/
|
|
5177
|
+
getProviderMetrics(providerName) {
|
|
5178
|
+
return this.providerMetrics.get(providerName);
|
|
5179
|
+
}
|
|
5180
|
+
/**
|
|
5181
|
+
* Get all provider metrics
|
|
5182
|
+
*/
|
|
5183
|
+
getAllProviderMetrics() {
|
|
5184
|
+
return Array.from(this.providerMetrics.values());
|
|
5185
|
+
}
|
|
5186
|
+
/**
|
|
5187
|
+
* Get MCP session metrics
|
|
5188
|
+
*/
|
|
5189
|
+
getMCPMetrics() {
|
|
5190
|
+
return { ...this.mcpMetrics };
|
|
5191
|
+
}
|
|
5192
|
+
/**
|
|
5193
|
+
* Get aggregated metrics summary
|
|
5194
|
+
*/
|
|
5195
|
+
getMetricsSummary() {
|
|
5196
|
+
let totalSDK = 0;
|
|
5197
|
+
let totalCLI = 0;
|
|
5198
|
+
let totalFallbacks = 0;
|
|
5199
|
+
let sdkLatencySum = 0;
|
|
5200
|
+
let cliLatencySum = 0;
|
|
5201
|
+
let sdkCount = 0;
|
|
5202
|
+
let cliCount = 0;
|
|
5203
|
+
const openCircuitBreakers = [];
|
|
5204
|
+
for (const data of this.providerMetrics.values()) {
|
|
5205
|
+
totalSDK += data.metrics.sdkExecutions;
|
|
5206
|
+
totalCLI += data.metrics.cliExecutions;
|
|
5207
|
+
totalFallbacks += data.metrics.sdkFallbacks;
|
|
5208
|
+
if (data.metrics.avgSdkLatencyMs > 0) {
|
|
5209
|
+
sdkLatencySum += data.metrics.avgSdkLatencyMs;
|
|
5210
|
+
sdkCount++;
|
|
5211
|
+
}
|
|
5212
|
+
if (data.metrics.avgCliLatencyMs > 0) {
|
|
5213
|
+
cliLatencySum += data.metrics.avgCliLatencyMs;
|
|
5214
|
+
cliCount++;
|
|
5215
|
+
}
|
|
5216
|
+
if (data.circuitBreakerState === "open") {
|
|
5217
|
+
openCircuitBreakers.push(data.providerName);
|
|
5218
|
+
}
|
|
5219
|
+
}
|
|
5220
|
+
return {
|
|
5221
|
+
totalSDKExecutions: totalSDK,
|
|
5222
|
+
totalCLIExecutions: totalCLI,
|
|
5223
|
+
totalMCPConnections: this.mcpMetrics.totalConnections,
|
|
5224
|
+
overallFallbackRate: totalSDK > 0 ? totalFallbacks / totalSDK : 0,
|
|
5225
|
+
avgSDKLatencyMs: sdkCount > 0 ? sdkLatencySum / sdkCount : 0,
|
|
5226
|
+
avgCLILatencyMs: cliCount > 0 ? cliLatencySum / cliCount : 0,
|
|
5227
|
+
providersWithOpenCircuitBreaker: openCircuitBreakers
|
|
5228
|
+
};
|
|
5229
|
+
}
|
|
5230
|
+
/**
|
|
5231
|
+
* Reset all metrics (for testing)
|
|
5232
|
+
*/
|
|
5233
|
+
reset() {
|
|
5234
|
+
this.providerMetrics.clear();
|
|
5235
|
+
this.sdkLatencies.clear();
|
|
5236
|
+
this.cliLatencies.clear();
|
|
5237
|
+
this.mcpLatencies = [];
|
|
5238
|
+
this.mcpMetrics = {
|
|
5239
|
+
totalConnections: 0,
|
|
5240
|
+
activeConnections: 0,
|
|
5241
|
+
failedConnections: 0,
|
|
5242
|
+
totalToolCalls: 0,
|
|
5243
|
+
avgToolCallLatencyMs: 0,
|
|
5244
|
+
sessionsByProvider: {}
|
|
5245
|
+
};
|
|
5246
|
+
}
|
|
5247
|
+
/**
|
|
5248
|
+
* Add latency sample to provider's latency array
|
|
5249
|
+
*/
|
|
5250
|
+
addLatencySample(latencyMap, providerName, latencyMs) {
|
|
5251
|
+
if (!latencyMap.has(providerName)) {
|
|
5252
|
+
latencyMap.set(providerName, []);
|
|
5253
|
+
}
|
|
5254
|
+
const latencies = latencyMap.get(providerName);
|
|
5255
|
+
latencies.push(latencyMs);
|
|
5256
|
+
if (latencies.length > this.MAX_LATENCY_SAMPLES) {
|
|
5257
|
+
latencies.shift();
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
5260
|
+
/**
|
|
5261
|
+
* Calculate average latency from samples
|
|
5262
|
+
*/
|
|
5263
|
+
calculateAverageLatency(latencies) {
|
|
5264
|
+
if (latencies.length === 0) return 0;
|
|
5265
|
+
const sum = latencies.reduce((a, b) => a + b, 0);
|
|
5266
|
+
return Math.round(sum / latencies.length);
|
|
5267
|
+
}
|
|
5268
|
+
/**
|
|
5269
|
+
* Calculate success rate
|
|
5270
|
+
*/
|
|
5271
|
+
calculateSuccessRate(total, errors) {
|
|
5272
|
+
if (total === 0) return 1;
|
|
5273
|
+
return Math.max(0, (total - errors) / total);
|
|
5274
|
+
}
|
|
5275
|
+
};
|
|
5276
|
+
}
|
|
5277
|
+
});
|
|
5278
|
+
|
|
5279
|
+
// src/providers/hybrid-adapter-base.ts
|
|
5280
|
+
var DEFAULT_CIRCUIT_BREAKER_CONFIG, CircuitBreaker2, HybridAdapterBase;
|
|
5281
|
+
var init_hybrid_adapter_base = __esm({
|
|
5282
|
+
"src/providers/hybrid-adapter-base.ts"() {
|
|
5283
|
+
init_esm_shims();
|
|
5284
|
+
init_logger();
|
|
5285
|
+
init_flags();
|
|
5286
|
+
init_fallback_decision();
|
|
5287
|
+
init_provider_metrics();
|
|
5288
|
+
DEFAULT_CIRCUIT_BREAKER_CONFIG = {
|
|
5289
|
+
failureThreshold: 3,
|
|
5290
|
+
resetTimeout: 6e4,
|
|
5291
|
+
// 1 minute
|
|
5292
|
+
halfOpenSuccessThreshold: 2
|
|
5293
|
+
};
|
|
5294
|
+
CircuitBreaker2 = class {
|
|
5295
|
+
state = "closed";
|
|
5296
|
+
failures = 0;
|
|
5297
|
+
successes = 0;
|
|
5298
|
+
lastFailureTime = 0;
|
|
5299
|
+
config;
|
|
5300
|
+
constructor(config = {}) {
|
|
5301
|
+
this.config = { ...DEFAULT_CIRCUIT_BREAKER_CONFIG, ...config };
|
|
5302
|
+
}
|
|
5303
|
+
/**
|
|
5304
|
+
* Check if circuit is open (requests should not be attempted)
|
|
5305
|
+
*/
|
|
5306
|
+
isOpen() {
|
|
5307
|
+
if (this.state === "open") {
|
|
5308
|
+
if (Date.now() - this.lastFailureTime >= this.config.resetTimeout) {
|
|
5309
|
+
this.state = "half-open";
|
|
5310
|
+
this.successes = 0;
|
|
5311
|
+
logger.debug("Circuit breaker transitioning to half-open");
|
|
5312
|
+
return false;
|
|
5313
|
+
}
|
|
5314
|
+
return true;
|
|
5315
|
+
}
|
|
5316
|
+
return false;
|
|
5317
|
+
}
|
|
5318
|
+
/**
|
|
5319
|
+
* Get current state
|
|
5320
|
+
*/
|
|
5321
|
+
getState() {
|
|
5322
|
+
if (this.state === "open" && Date.now() - this.lastFailureTime >= this.config.resetTimeout) {
|
|
5323
|
+
this.state = "half-open";
|
|
5324
|
+
this.successes = 0;
|
|
5325
|
+
}
|
|
5326
|
+
return this.state;
|
|
5327
|
+
}
|
|
5328
|
+
/**
|
|
5329
|
+
* Record successful execution
|
|
5330
|
+
*/
|
|
5331
|
+
recordSuccess() {
|
|
5332
|
+
if (this.state === "half-open") {
|
|
5333
|
+
this.successes++;
|
|
5334
|
+
if (this.successes >= this.config.halfOpenSuccessThreshold) {
|
|
5335
|
+
this.state = "closed";
|
|
5336
|
+
this.failures = 0;
|
|
5337
|
+
this.successes = 0;
|
|
5338
|
+
logger.debug("Circuit breaker closed after recovery");
|
|
5339
|
+
}
|
|
5340
|
+
} else if (this.state === "closed") {
|
|
5341
|
+
this.failures = 0;
|
|
5342
|
+
}
|
|
5343
|
+
}
|
|
5344
|
+
/**
|
|
5345
|
+
* Record failed execution
|
|
5346
|
+
*/
|
|
5347
|
+
recordFailure() {
|
|
5348
|
+
this.failures++;
|
|
5349
|
+
this.lastFailureTime = Date.now();
|
|
5350
|
+
if (this.state === "half-open") {
|
|
5351
|
+
this.state = "open";
|
|
5352
|
+
logger.warn("Circuit breaker opened after half-open failure");
|
|
5353
|
+
} else if (this.failures >= this.config.failureThreshold) {
|
|
5354
|
+
this.state = "open";
|
|
5355
|
+
logger.warn("Circuit breaker opened after consecutive failures", {
|
|
5356
|
+
failures: this.failures,
|
|
5357
|
+
threshold: this.config.failureThreshold
|
|
5358
|
+
});
|
|
5359
|
+
}
|
|
5360
|
+
}
|
|
5361
|
+
/**
|
|
5362
|
+
* Reset circuit breaker to closed state
|
|
5363
|
+
*/
|
|
5364
|
+
reset() {
|
|
5365
|
+
this.state = "closed";
|
|
5366
|
+
this.failures = 0;
|
|
5367
|
+
this.successes = 0;
|
|
5368
|
+
this.lastFailureTime = 0;
|
|
5369
|
+
}
|
|
5370
|
+
};
|
|
5371
|
+
HybridAdapterBase = class {
|
|
5372
|
+
mode;
|
|
5373
|
+
activeMode = null;
|
|
5374
|
+
providerName;
|
|
5375
|
+
sdkCircuitBreaker;
|
|
5376
|
+
cliCircuitBreaker;
|
|
5377
|
+
maxRetries;
|
|
5378
|
+
// Track initialization state
|
|
5379
|
+
sdkInitialized = false;
|
|
5380
|
+
cliInitialized = false;
|
|
5381
|
+
initPromise = null;
|
|
5382
|
+
constructor(options) {
|
|
5383
|
+
this.mode = options.mode || "auto";
|
|
5384
|
+
this.providerName = options.providerName;
|
|
5385
|
+
this.maxRetries = options.maxRetries ?? 1;
|
|
5386
|
+
this.sdkCircuitBreaker = new CircuitBreaker2(options.sdkCircuitBreaker);
|
|
5387
|
+
this.cliCircuitBreaker = new CircuitBreaker2(options.cliCircuitBreaker);
|
|
5388
|
+
logger.debug("HybridAdapterBase initialized", {
|
|
5389
|
+
provider: this.providerName,
|
|
5390
|
+
mode: this.mode,
|
|
5391
|
+
maxRetries: this.maxRetries
|
|
5392
|
+
});
|
|
5393
|
+
}
|
|
5394
|
+
/**
|
|
5395
|
+
* Execute a request using the appropriate mode
|
|
5396
|
+
*
|
|
5397
|
+
* Mode selection logic:
|
|
5398
|
+
* 1. If mode is 'cli' or SDK-first is disabled → use CLI directly
|
|
5399
|
+
* 2. If mode is 'sdk' → use SDK only (throw if unavailable)
|
|
5400
|
+
* 3. If mode is 'auto' → try SDK first, fallback to CLI
|
|
5401
|
+
*/
|
|
5402
|
+
async execute(request) {
|
|
5403
|
+
await this.ensureInitialized();
|
|
5404
|
+
const useSDKFirst = this.shouldUseSDKFirst();
|
|
5405
|
+
if (!useSDKFirst || this.mode === "cli") {
|
|
5406
|
+
return this.executeWithCLI(request);
|
|
5407
|
+
}
|
|
5408
|
+
if (this.mode === "sdk") {
|
|
5409
|
+
return this.executeWithSDK(request);
|
|
5410
|
+
}
|
|
5411
|
+
return this.executeWithFallback(request);
|
|
5412
|
+
}
|
|
5413
|
+
/**
|
|
5414
|
+
* Execute with SDK-first and CLI fallback
|
|
5415
|
+
*/
|
|
5416
|
+
async executeWithFallback(request) {
|
|
5417
|
+
if (this.sdkCircuitBreaker.isOpen()) {
|
|
5418
|
+
logger.debug("SDK circuit breaker is open, using CLI", {
|
|
5419
|
+
provider: this.providerName
|
|
5420
|
+
});
|
|
5421
|
+
return this.executeWithCLI(request);
|
|
5422
|
+
}
|
|
5423
|
+
const startTime = Date.now();
|
|
5424
|
+
let lastError;
|
|
5425
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
5426
|
+
try {
|
|
5427
|
+
const result = await this.executeViaSDK(request);
|
|
5428
|
+
this.sdkCircuitBreaker.recordSuccess();
|
|
5429
|
+
this.recordSDKMetrics(Date.now() - startTime, true);
|
|
5430
|
+
return result;
|
|
5431
|
+
} catch (error) {
|
|
5432
|
+
lastError = error;
|
|
5433
|
+
const classification = decideFallback(error, this.providerName);
|
|
5434
|
+
logger.debug("SDK execution failed", {
|
|
5435
|
+
provider: this.providerName,
|
|
5436
|
+
attempt: attempt + 1,
|
|
5437
|
+
decision: classification.decision,
|
|
5438
|
+
reason: classification.reason
|
|
5439
|
+
});
|
|
5440
|
+
if (classification.decision === "retry_sdk" /* RETRY_SDK */ && attempt < this.maxRetries) {
|
|
5441
|
+
const delay = classification.retryDelayMs || 1e3;
|
|
5442
|
+
await this.sleep(delay);
|
|
5443
|
+
continue;
|
|
5444
|
+
}
|
|
5445
|
+
if (classification.decision === "use_cli" /* USE_CLI */ || classification.decision === "retry_sdk" /* RETRY_SDK */ && attempt >= this.maxRetries) {
|
|
5446
|
+
this.sdkCircuitBreaker.recordFailure();
|
|
5447
|
+
this.recordSDKMetrics(Date.now() - startTime, false);
|
|
5448
|
+
this.recordFallbackMetrics(classification.reason || "max retries exceeded");
|
|
5449
|
+
if (isSDKFallbackEnabled()) {
|
|
5450
|
+
logger.info("Falling back to CLI execution", {
|
|
5451
|
+
provider: this.providerName,
|
|
5452
|
+
reason: classification.reason || "max retries exceeded"
|
|
5453
|
+
});
|
|
5454
|
+
return this.executeWithCLI(request);
|
|
5455
|
+
}
|
|
5456
|
+
}
|
|
5457
|
+
this.sdkCircuitBreaker.recordFailure();
|
|
5458
|
+
this.recordSDKMetrics(Date.now() - startTime, false);
|
|
5459
|
+
throw error;
|
|
5460
|
+
}
|
|
5461
|
+
}
|
|
5462
|
+
this.sdkCircuitBreaker.recordFailure();
|
|
5463
|
+
this.recordSDKMetrics(Date.now() - startTime, false);
|
|
5464
|
+
if (isSDKFallbackEnabled()) {
|
|
5465
|
+
this.recordFallbackMetrics("max retries exceeded");
|
|
5466
|
+
return this.executeWithCLI(request);
|
|
5467
|
+
}
|
|
5468
|
+
throw lastError || new Error(`SDK execution failed after ${this.maxRetries + 1} attempts`);
|
|
5469
|
+
}
|
|
5470
|
+
/**
|
|
5471
|
+
* Execute using SDK only
|
|
5472
|
+
*/
|
|
5473
|
+
async executeWithSDK(request) {
|
|
5474
|
+
if (this.sdkCircuitBreaker.isOpen()) {
|
|
5475
|
+
throw new Error(`SDK circuit breaker is open for ${this.providerName}`);
|
|
5476
|
+
}
|
|
5477
|
+
const startTime = Date.now();
|
|
5478
|
+
try {
|
|
5479
|
+
const result = await this.executeViaSDK(request);
|
|
5480
|
+
this.sdkCircuitBreaker.recordSuccess();
|
|
5481
|
+
this.recordSDKMetrics(Date.now() - startTime, true);
|
|
5482
|
+
return result;
|
|
5483
|
+
} catch (error) {
|
|
5484
|
+
this.sdkCircuitBreaker.recordFailure();
|
|
5485
|
+
this.recordSDKMetrics(Date.now() - startTime, false);
|
|
5486
|
+
throw error;
|
|
5487
|
+
}
|
|
5488
|
+
}
|
|
5489
|
+
/**
|
|
5490
|
+
* Execute using CLI only
|
|
5491
|
+
*/
|
|
5492
|
+
async executeWithCLI(request) {
|
|
5493
|
+
if (this.cliCircuitBreaker.isOpen()) {
|
|
5494
|
+
throw new Error(`CLI circuit breaker is open for ${this.providerName}`);
|
|
5495
|
+
}
|
|
5496
|
+
const startTime = Date.now();
|
|
5497
|
+
try {
|
|
5498
|
+
const result = await this.executeViaCLI(request);
|
|
5499
|
+
this.cliCircuitBreaker.recordSuccess();
|
|
5500
|
+
this.recordCLIMetrics(Date.now() - startTime, true);
|
|
5501
|
+
return result;
|
|
5502
|
+
} catch (error) {
|
|
5503
|
+
this.cliCircuitBreaker.recordFailure();
|
|
5504
|
+
this.recordCLIMetrics(Date.now() - startTime, false);
|
|
5505
|
+
throw error;
|
|
5506
|
+
}
|
|
5507
|
+
}
|
|
5508
|
+
/**
|
|
5509
|
+
* Check if SDK-first mode should be used
|
|
5510
|
+
*/
|
|
5511
|
+
shouldUseSDKFirst() {
|
|
5512
|
+
if (!isSDKFirstModeEnabled()) {
|
|
5513
|
+
return false;
|
|
5514
|
+
}
|
|
5515
|
+
if (this.mode === "cli") {
|
|
5516
|
+
return false;
|
|
5517
|
+
}
|
|
5518
|
+
return true;
|
|
5519
|
+
}
|
|
5520
|
+
/**
|
|
5521
|
+
* Ensure adapters are initialized
|
|
5522
|
+
*/
|
|
5523
|
+
async ensureInitialized() {
|
|
5524
|
+
if (this.initPromise) {
|
|
5525
|
+
return this.initPromise;
|
|
5526
|
+
}
|
|
5527
|
+
this.initPromise = this.initialize();
|
|
5528
|
+
return this.initPromise;
|
|
5529
|
+
}
|
|
5530
|
+
/**
|
|
5531
|
+
* Initialize adapters based on mode
|
|
5532
|
+
*/
|
|
5533
|
+
async initialize() {
|
|
5534
|
+
const useSDKFirst = this.shouldUseSDKFirst();
|
|
5535
|
+
if (this.mode === "cli" || !useSDKFirst) {
|
|
5536
|
+
if (!this.cliInitialized) {
|
|
5537
|
+
const available = await this.isCLIAvailable();
|
|
5538
|
+
if (!available) {
|
|
5539
|
+
throw new Error(`CLI not available for ${this.providerName}`);
|
|
5540
|
+
}
|
|
5541
|
+
await this.initializeCLI();
|
|
5542
|
+
this.cliInitialized = true;
|
|
5543
|
+
this.activeMode = "cli";
|
|
5544
|
+
}
|
|
5545
|
+
return;
|
|
5546
|
+
}
|
|
5547
|
+
if (this.mode === "sdk") {
|
|
5548
|
+
if (!this.sdkInitialized) {
|
|
5549
|
+
const available = await this.isSDKAvailable();
|
|
5550
|
+
if (!available) {
|
|
5551
|
+
throw new Error(`SDK not available for ${this.providerName}`);
|
|
5552
|
+
}
|
|
5553
|
+
await this.initializeSDK();
|
|
5554
|
+
this.sdkInitialized = true;
|
|
5555
|
+
this.activeMode = "sdk";
|
|
5556
|
+
}
|
|
5557
|
+
return;
|
|
5558
|
+
}
|
|
5559
|
+
if (!this.sdkInitialized) {
|
|
5560
|
+
const sdkAvailable = await this.isSDKAvailable();
|
|
5561
|
+
if (sdkAvailable) {
|
|
5562
|
+
try {
|
|
5563
|
+
await this.initializeSDK();
|
|
5564
|
+
this.sdkInitialized = true;
|
|
5565
|
+
this.activeMode = "sdk";
|
|
5566
|
+
} catch (error) {
|
|
5567
|
+
logger.warn("SDK initialization failed, will use CLI", {
|
|
5568
|
+
provider: this.providerName,
|
|
5569
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5570
|
+
});
|
|
5571
|
+
}
|
|
5572
|
+
}
|
|
5573
|
+
}
|
|
5574
|
+
if (!this.cliInitialized) {
|
|
5575
|
+
const cliAvailable = await this.isCLIAvailable();
|
|
5576
|
+
if (cliAvailable) {
|
|
5577
|
+
await this.initializeCLI();
|
|
5578
|
+
this.cliInitialized = true;
|
|
5579
|
+
if (!this.sdkInitialized) {
|
|
5580
|
+
this.activeMode = "cli";
|
|
5581
|
+
}
|
|
5582
|
+
} else if (!this.sdkInitialized) {
|
|
5583
|
+
throw new Error(`Neither SDK nor CLI available for ${this.providerName}`);
|
|
5584
|
+
}
|
|
5585
|
+
}
|
|
5586
|
+
}
|
|
5587
|
+
/**
|
|
5588
|
+
* Record SDK execution metrics
|
|
5589
|
+
*/
|
|
5590
|
+
recordSDKMetrics(latencyMs, success) {
|
|
5591
|
+
try {
|
|
5592
|
+
const collector = getProviderMetrics();
|
|
5593
|
+
collector.recordSDKExecution(this.providerName, latencyMs, success);
|
|
5594
|
+
} catch {
|
|
5595
|
+
}
|
|
5596
|
+
}
|
|
5597
|
+
/**
|
|
5598
|
+
* Record CLI execution metrics
|
|
5599
|
+
*/
|
|
5600
|
+
recordCLIMetrics(latencyMs, success) {
|
|
5601
|
+
try {
|
|
5602
|
+
const collector = getProviderMetrics();
|
|
5603
|
+
collector.recordCLIExecution(this.providerName, latencyMs, success);
|
|
5604
|
+
} catch {
|
|
5605
|
+
}
|
|
5606
|
+
}
|
|
5607
|
+
/**
|
|
5608
|
+
* Record fallback event metrics
|
|
5609
|
+
*/
|
|
5610
|
+
recordFallbackMetrics(reason) {
|
|
5611
|
+
try {
|
|
5612
|
+
const collector = getProviderMetrics();
|
|
5613
|
+
collector.recordSDKFallback(this.providerName, reason);
|
|
5614
|
+
} catch {
|
|
5615
|
+
}
|
|
5616
|
+
}
|
|
5617
|
+
/**
|
|
5618
|
+
* Sleep utility
|
|
5619
|
+
*/
|
|
5620
|
+
sleep(ms) {
|
|
5621
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
5622
|
+
}
|
|
5623
|
+
/**
|
|
5624
|
+
* Get current active mode
|
|
5625
|
+
*/
|
|
5626
|
+
getActiveMode() {
|
|
5627
|
+
return this.activeMode;
|
|
5628
|
+
}
|
|
5629
|
+
/**
|
|
5630
|
+
* Get SDK circuit breaker state
|
|
5631
|
+
*/
|
|
5632
|
+
getSDKCircuitBreakerState() {
|
|
5633
|
+
return this.sdkCircuitBreaker.getState();
|
|
5634
|
+
}
|
|
5635
|
+
/**
|
|
5636
|
+
* Get CLI circuit breaker state
|
|
5637
|
+
*/
|
|
5638
|
+
getCLICircuitBreakerState() {
|
|
5639
|
+
return this.cliCircuitBreaker.getState();
|
|
5640
|
+
}
|
|
5641
|
+
/**
|
|
5642
|
+
* Reset both circuit breakers
|
|
5643
|
+
*/
|
|
5644
|
+
resetCircuitBreakers() {
|
|
5645
|
+
this.sdkCircuitBreaker.reset();
|
|
5646
|
+
this.cliCircuitBreaker.reset();
|
|
5647
|
+
logger.debug("Circuit breakers reset", { provider: this.providerName });
|
|
5648
|
+
}
|
|
5649
|
+
/**
|
|
5650
|
+
* Clean up resources
|
|
5651
|
+
*/
|
|
5652
|
+
async destroy() {
|
|
5653
|
+
await this.destroySDK();
|
|
5654
|
+
await this.destroyCLI();
|
|
5655
|
+
this.sdkInitialized = false;
|
|
5656
|
+
this.cliInitialized = false;
|
|
5657
|
+
this.activeMode = null;
|
|
5658
|
+
this.initPromise = null;
|
|
5659
|
+
}
|
|
5660
|
+
};
|
|
5661
|
+
}
|
|
5662
|
+
});
|
|
5663
|
+
|
|
5664
|
+
// src/integrations/ax-glm/types.ts
|
|
5665
|
+
function normalizeGLMModel(model) {
|
|
5666
|
+
return GLM_MODEL_MAPPING[model] || model;
|
|
5667
|
+
}
|
|
5668
|
+
var GLM_MODEL_MAPPING, GLM_DEFAULT_BASE_URL, GLM_DEFAULT_MODEL, GLM_DEFAULT_COMMAND;
|
|
5669
|
+
var init_types2 = __esm({
|
|
5670
|
+
"src/integrations/ax-glm/types.ts"() {
|
|
5671
|
+
init_esm_shims();
|
|
5672
|
+
GLM_MODEL_MAPPING = {
|
|
5673
|
+
"glm-4-plus": "glm-4.6",
|
|
5674
|
+
"glm-4v": "glm-4.5v",
|
|
5675
|
+
"glm-4-air": "glm-4-flash",
|
|
5676
|
+
"glm-4-airx": "glm-4-flash"
|
|
5677
|
+
};
|
|
5678
|
+
GLM_DEFAULT_BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
|
|
5679
|
+
GLM_DEFAULT_MODEL = "glm-4";
|
|
5680
|
+
GLM_DEFAULT_COMMAND = "ax-glm";
|
|
5681
|
+
}
|
|
5682
|
+
});
|
|
5683
|
+
|
|
5684
|
+
// src/integrations/ax-glm/sdk-adapter.ts
|
|
5685
|
+
var GLMSdkAdapter;
|
|
5686
|
+
var init_sdk_adapter2 = __esm({
|
|
5687
|
+
"src/integrations/ax-glm/sdk-adapter.ts"() {
|
|
5688
|
+
init_esm_shims();
|
|
5689
|
+
init_logger();
|
|
5690
|
+
init_validation_limits();
|
|
5691
|
+
init_types2();
|
|
5692
|
+
GLMSdkAdapter = class {
|
|
5693
|
+
client = null;
|
|
5694
|
+
config;
|
|
5695
|
+
initialized = false;
|
|
5696
|
+
constructor(config = {}) {
|
|
5697
|
+
this.config = {
|
|
5698
|
+
apiKey: config.apiKey || process.env.ZAI_API_KEY || "",
|
|
5699
|
+
baseUrl: config.baseUrl || GLM_DEFAULT_BASE_URL,
|
|
5700
|
+
model: config.model || GLM_DEFAULT_MODEL,
|
|
5701
|
+
timeout: config.timeout || TIMEOUTS.PROVIDER_DEFAULT
|
|
5702
|
+
};
|
|
5703
|
+
logger.debug("[GLM SDK] Adapter created", {
|
|
5704
|
+
model: this.config.model,
|
|
5705
|
+
baseUrl: this.config.baseUrl,
|
|
5706
|
+
hasApiKey: !!this.config.apiKey
|
|
5707
|
+
});
|
|
5708
|
+
}
|
|
5709
|
+
/**
|
|
5710
|
+
* Check if SDK is available (OpenAI package installed)
|
|
5711
|
+
*/
|
|
5712
|
+
async isAvailable() {
|
|
5713
|
+
try {
|
|
5714
|
+
if (!this.config.apiKey) {
|
|
5715
|
+
logger.debug("[GLM SDK] No API key configured");
|
|
5716
|
+
return false;
|
|
5717
|
+
}
|
|
5718
|
+
await import('openai');
|
|
5719
|
+
return true;
|
|
5720
|
+
} catch (error) {
|
|
5721
|
+
logger.debug("[GLM SDK] OpenAI SDK not available", {
|
|
5722
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5723
|
+
});
|
|
5724
|
+
return false;
|
|
5725
|
+
}
|
|
5726
|
+
}
|
|
5727
|
+
/**
|
|
5728
|
+
* Initialize the SDK client
|
|
5729
|
+
*/
|
|
5730
|
+
async initialize() {
|
|
5731
|
+
if (this.initialized) {
|
|
5732
|
+
return;
|
|
5733
|
+
}
|
|
5734
|
+
try {
|
|
5735
|
+
const OpenAI = (await import('openai')).default;
|
|
5736
|
+
this.client = new OpenAI({
|
|
5737
|
+
apiKey: this.config.apiKey,
|
|
5738
|
+
baseURL: this.config.baseUrl,
|
|
5739
|
+
timeout: this.config.timeout
|
|
5740
|
+
});
|
|
5741
|
+
this.initialized = true;
|
|
5742
|
+
logger.debug("[GLM SDK] Client initialized", {
|
|
5743
|
+
model: this.config.model
|
|
5744
|
+
});
|
|
5745
|
+
} catch (error) {
|
|
5746
|
+
throw new Error(
|
|
5747
|
+
`Failed to initialize GLM SDK: ${error instanceof Error ? error.message : String(error)}`
|
|
5748
|
+
);
|
|
5749
|
+
}
|
|
5750
|
+
}
|
|
5751
|
+
/**
|
|
5752
|
+
* Execute a request using the GLM SDK
|
|
5753
|
+
*/
|
|
5754
|
+
async execute(request) {
|
|
5755
|
+
if (!this.initialized) {
|
|
5756
|
+
await this.initialize();
|
|
5757
|
+
}
|
|
5758
|
+
const startTime = Date.now();
|
|
5759
|
+
try {
|
|
5760
|
+
const messages = [];
|
|
5761
|
+
if (request.systemPrompt) {
|
|
5762
|
+
messages.push({
|
|
5763
|
+
role: "system",
|
|
5764
|
+
content: request.systemPrompt
|
|
5765
|
+
});
|
|
5766
|
+
}
|
|
5767
|
+
messages.push({
|
|
5768
|
+
role: "user",
|
|
5769
|
+
content: request.prompt
|
|
5770
|
+
});
|
|
5771
|
+
const model = normalizeGLMModel(this.config.model);
|
|
5772
|
+
logger.debug("[GLM SDK] Executing request", {
|
|
5773
|
+
model,
|
|
5774
|
+
messageCount: messages.length,
|
|
5775
|
+
promptLength: request.prompt.length
|
|
5776
|
+
});
|
|
5777
|
+
const openaiClient = this.client;
|
|
5778
|
+
const response = await openaiClient.chat.completions.create({
|
|
5779
|
+
model,
|
|
5780
|
+
messages,
|
|
5781
|
+
max_tokens: request.maxTokens,
|
|
5782
|
+
temperature: request.temperature,
|
|
5783
|
+
stream: false
|
|
5784
|
+
});
|
|
5785
|
+
const latencyMs = Date.now() - startTime;
|
|
5786
|
+
if (!response.choices || response.choices.length === 0) {
|
|
5787
|
+
throw new Error("GLM API returned empty choices array");
|
|
5788
|
+
}
|
|
5789
|
+
const choice = response.choices[0];
|
|
5790
|
+
const content = choice?.message?.content || "";
|
|
5791
|
+
const finishReason = choice?.finish_reason || "unknown";
|
|
5792
|
+
logger.debug("[GLM SDK] Request completed", {
|
|
5793
|
+
model: response.model,
|
|
5794
|
+
latencyMs,
|
|
5795
|
+
tokensUsed: response.usage?.total_tokens
|
|
5796
|
+
});
|
|
5797
|
+
return {
|
|
5798
|
+
content,
|
|
5799
|
+
model: response.model,
|
|
5800
|
+
tokensUsed: response.usage ? {
|
|
5801
|
+
prompt: response.usage.prompt_tokens,
|
|
5802
|
+
completion: response.usage.completion_tokens,
|
|
5803
|
+
total: response.usage.total_tokens
|
|
5804
|
+
} : { prompt: 0, completion: 0, total: 0 },
|
|
5805
|
+
latencyMs,
|
|
5806
|
+
finishReason,
|
|
5807
|
+
cached: false
|
|
5808
|
+
};
|
|
5809
|
+
} catch (error) {
|
|
5810
|
+
const latencyMs = Date.now() - startTime;
|
|
5811
|
+
logger.error("[GLM SDK] Request failed", {
|
|
5812
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5813
|
+
latencyMs
|
|
5814
|
+
});
|
|
5815
|
+
throw error;
|
|
5816
|
+
}
|
|
5817
|
+
}
|
|
5818
|
+
/**
|
|
5819
|
+
* Get the configured model
|
|
5820
|
+
*/
|
|
5821
|
+
getModel() {
|
|
5822
|
+
return this.config.model;
|
|
5823
|
+
}
|
|
5824
|
+
/**
|
|
5825
|
+
* Clean up resources
|
|
5826
|
+
*/
|
|
5827
|
+
async destroy() {
|
|
5828
|
+
this.client = null;
|
|
5829
|
+
this.initialized = false;
|
|
5830
|
+
logger.debug("[GLM SDK] Adapter destroyed");
|
|
5831
|
+
}
|
|
5832
|
+
};
|
|
5833
|
+
}
|
|
5834
|
+
});
|
|
5835
|
+
var execAsync, GLMCliWrapper;
|
|
5836
|
+
var init_cli_wrapper2 = __esm({
|
|
5837
|
+
"src/integrations/ax-glm/cli-wrapper.ts"() {
|
|
5838
|
+
init_esm_shims();
|
|
5839
|
+
init_logger();
|
|
5840
|
+
init_validation_limits();
|
|
5841
|
+
init_types2();
|
|
5842
|
+
execAsync = promisify(exec);
|
|
5843
|
+
GLMCliWrapper = class {
|
|
5844
|
+
config;
|
|
5845
|
+
cliPath = null;
|
|
5846
|
+
cliVersion = null;
|
|
5847
|
+
constructor(config = {}) {
|
|
5848
|
+
this.config = {
|
|
5849
|
+
command: config.command || GLM_DEFAULT_COMMAND,
|
|
5850
|
+
model: config.model || GLM_DEFAULT_MODEL,
|
|
5851
|
+
timeout: config.timeout || TIMEOUTS.PROVIDER_DEFAULT
|
|
5852
|
+
};
|
|
5853
|
+
logger.debug("[GLM CLI] Wrapper created", {
|
|
5854
|
+
command: this.config.command,
|
|
5855
|
+
model: this.config.model
|
|
5856
|
+
});
|
|
5857
|
+
}
|
|
5858
|
+
/**
|
|
5859
|
+
* Check if CLI is available
|
|
5860
|
+
*/
|
|
5861
|
+
async isAvailable() {
|
|
5862
|
+
try {
|
|
5863
|
+
const { stdout } = await execAsync(`which ${this.config.command}`, {
|
|
5864
|
+
timeout: TIMEOUTS.PROVIDER_DETECTION
|
|
5865
|
+
});
|
|
5866
|
+
this.cliPath = stdout.trim();
|
|
5867
|
+
if (this.cliPath) {
|
|
5868
|
+
try {
|
|
5869
|
+
const { stdout: versionOutput } = await execAsync(
|
|
5870
|
+
`${this.config.command} --version`,
|
|
5871
|
+
{ timeout: TIMEOUTS.PROVIDER_DETECTION }
|
|
5872
|
+
);
|
|
5873
|
+
this.cliVersion = versionOutput.trim();
|
|
5874
|
+
} catch {
|
|
5875
|
+
this.cliVersion = "unknown";
|
|
5876
|
+
}
|
|
5877
|
+
logger.debug("[GLM CLI] CLI available", {
|
|
5878
|
+
path: this.cliPath,
|
|
5879
|
+
version: this.cliVersion
|
|
5880
|
+
});
|
|
5881
|
+
return true;
|
|
5882
|
+
}
|
|
5883
|
+
return false;
|
|
5884
|
+
} catch (error) {
|
|
5885
|
+
logger.debug("[GLM CLI] CLI not available", {
|
|
5886
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5887
|
+
});
|
|
5888
|
+
return false;
|
|
5889
|
+
}
|
|
5890
|
+
}
|
|
5891
|
+
/**
|
|
5892
|
+
* Initialize the CLI wrapper
|
|
5893
|
+
*/
|
|
5894
|
+
async initialize() {
|
|
5895
|
+
const available = await this.isAvailable();
|
|
5896
|
+
if (!available) {
|
|
5897
|
+
throw new Error(`${this.config.command} CLI is not installed or not in PATH`);
|
|
5898
|
+
}
|
|
5899
|
+
}
|
|
5900
|
+
/**
|
|
5901
|
+
* Execute a request using the CLI
|
|
5902
|
+
*/
|
|
5903
|
+
async execute(request) {
|
|
5904
|
+
const startTime = Date.now();
|
|
5905
|
+
try {
|
|
5906
|
+
const args2 = this.buildArgs(request);
|
|
5907
|
+
logger.debug("[GLM CLI] Executing", {
|
|
5908
|
+
command: this.config.command,
|
|
5909
|
+
args: args2,
|
|
5910
|
+
promptLength: request.prompt.length
|
|
5911
|
+
});
|
|
5912
|
+
const result = await this.spawnCLI(args2, request.prompt);
|
|
5913
|
+
const latencyMs = Date.now() - startTime;
|
|
5914
|
+
const response = this.parseResponse(result);
|
|
5915
|
+
logger.debug("[GLM CLI] Request completed", {
|
|
5916
|
+
latencyMs,
|
|
5917
|
+
contentLength: response.content.length
|
|
5918
|
+
});
|
|
5919
|
+
return {
|
|
5920
|
+
...response,
|
|
5921
|
+
latencyMs,
|
|
5922
|
+
cached: false
|
|
5923
|
+
};
|
|
5924
|
+
} catch (error) {
|
|
5925
|
+
const latencyMs = Date.now() - startTime;
|
|
5926
|
+
logger.error("[GLM CLI] Request failed", {
|
|
5927
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5928
|
+
latencyMs
|
|
5929
|
+
});
|
|
5930
|
+
throw error;
|
|
5931
|
+
}
|
|
5932
|
+
}
|
|
5933
|
+
/**
|
|
5934
|
+
* Build CLI arguments
|
|
5935
|
+
*/
|
|
5936
|
+
buildArgs(request) {
|
|
5937
|
+
const args2 = ["-p"];
|
|
5938
|
+
const model = normalizeGLMModel(this.config.model);
|
|
5939
|
+
if (model !== "glm-4") {
|
|
5940
|
+
args2.push("--model", model);
|
|
5941
|
+
}
|
|
5942
|
+
if (request.systemPrompt) {
|
|
5943
|
+
args2.push("--system", request.systemPrompt);
|
|
5944
|
+
}
|
|
5945
|
+
if (request.maxTokens) {
|
|
5946
|
+
args2.push("--max-tokens", String(request.maxTokens));
|
|
5947
|
+
}
|
|
5948
|
+
if (request.temperature !== void 0) {
|
|
5949
|
+
args2.push("--temperature", String(request.temperature));
|
|
5950
|
+
}
|
|
5951
|
+
return args2;
|
|
5952
|
+
}
|
|
5953
|
+
/**
|
|
5954
|
+
* Spawn CLI process and get output
|
|
5955
|
+
*/
|
|
5956
|
+
spawnCLI(args2, prompt) {
|
|
5957
|
+
return new Promise((resolve5, reject) => {
|
|
5958
|
+
const process2 = spawn(this.config.command, args2, {
|
|
5959
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5960
|
+
timeout: this.config.timeout
|
|
5961
|
+
});
|
|
5962
|
+
let stdout = "";
|
|
5963
|
+
let stderr = "";
|
|
5964
|
+
process2.stdout.on("data", (data) => {
|
|
5965
|
+
stdout += data.toString();
|
|
5966
|
+
});
|
|
5967
|
+
process2.stderr.on("data", (data) => {
|
|
5968
|
+
stderr += data.toString();
|
|
5969
|
+
});
|
|
5970
|
+
process2.on("error", (error) => {
|
|
5971
|
+
reject(new Error(`CLI process error: ${error.message}`));
|
|
5972
|
+
});
|
|
5973
|
+
process2.on("close", (code) => {
|
|
5974
|
+
if (code === 0) {
|
|
5975
|
+
resolve5(stdout);
|
|
5976
|
+
} else {
|
|
5977
|
+
reject(new Error(
|
|
5978
|
+
`CLI exited with code ${code}: ${stderr || "No error message"}`
|
|
5979
|
+
));
|
|
5980
|
+
}
|
|
5981
|
+
});
|
|
5982
|
+
process2.stdin.write(prompt);
|
|
5983
|
+
process2.stdin.end();
|
|
5984
|
+
});
|
|
5985
|
+
}
|
|
5986
|
+
/**
|
|
5987
|
+
* Parse CLI response
|
|
5988
|
+
*/
|
|
5989
|
+
parseResponse(output) {
|
|
5990
|
+
try {
|
|
5991
|
+
const parsed = JSON.parse(output.trim());
|
|
5992
|
+
if (parsed.content || parsed.message) {
|
|
5993
|
+
return {
|
|
5994
|
+
content: parsed.content || parsed.message || "",
|
|
5995
|
+
model: parsed.model || normalizeGLMModel(this.config.model),
|
|
5996
|
+
tokensUsed: parsed.usage ? {
|
|
5997
|
+
prompt: parsed.usage.prompt_tokens || 0,
|
|
5998
|
+
completion: parsed.usage.completion_tokens || 0,
|
|
5999
|
+
total: parsed.usage.total_tokens || 0
|
|
6000
|
+
} : { prompt: 0, completion: 0, total: 0 },
|
|
6001
|
+
finishReason: parsed.finish_reason || "stop"
|
|
6002
|
+
};
|
|
6003
|
+
}
|
|
6004
|
+
} catch {
|
|
6005
|
+
}
|
|
6006
|
+
return {
|
|
6007
|
+
content: output.trim(),
|
|
6008
|
+
model: normalizeGLMModel(this.config.model),
|
|
6009
|
+
tokensUsed: { prompt: 0, completion: 0, total: 0 },
|
|
6010
|
+
finishReason: "stop"
|
|
6011
|
+
};
|
|
6012
|
+
}
|
|
6013
|
+
/**
|
|
6014
|
+
* Get the configured model
|
|
6015
|
+
*/
|
|
6016
|
+
getModel() {
|
|
6017
|
+
return this.config.model;
|
|
6018
|
+
}
|
|
6019
|
+
/**
|
|
6020
|
+
* Get CLI version
|
|
6021
|
+
*/
|
|
6022
|
+
getVersion() {
|
|
6023
|
+
return this.cliVersion;
|
|
6024
|
+
}
|
|
6025
|
+
/**
|
|
6026
|
+
* Get CLI command
|
|
6027
|
+
*/
|
|
6028
|
+
getCommand() {
|
|
6029
|
+
return this.config.command;
|
|
6030
|
+
}
|
|
6031
|
+
/**
|
|
6032
|
+
* Clean up resources
|
|
6033
|
+
*/
|
|
6034
|
+
async destroy() {
|
|
6035
|
+
logger.debug("[GLM CLI] Wrapper destroyed");
|
|
6036
|
+
}
|
|
6037
|
+
};
|
|
6038
|
+
}
|
|
6039
|
+
});
|
|
6040
|
+
|
|
6041
|
+
// src/integrations/ax-glm/hybrid-adapter.ts
|
|
6042
|
+
var GLMHybridAdapter;
|
|
6043
|
+
var init_hybrid_adapter2 = __esm({
|
|
6044
|
+
"src/integrations/ax-glm/hybrid-adapter.ts"() {
|
|
6045
|
+
init_esm_shims();
|
|
6046
|
+
init_hybrid_adapter_base();
|
|
6047
|
+
init_sdk_adapter2();
|
|
6048
|
+
init_cli_wrapper2();
|
|
6049
|
+
init_types2();
|
|
6050
|
+
init_logger();
|
|
6051
|
+
GLMHybridAdapter = class extends HybridAdapterBase {
|
|
6052
|
+
sdkAdapter = null;
|
|
6053
|
+
cliWrapper = null;
|
|
6054
|
+
model;
|
|
6055
|
+
sdkConfig;
|
|
6056
|
+
cliConfig;
|
|
6057
|
+
constructor(options = {}) {
|
|
6058
|
+
const baseOptions = {
|
|
6059
|
+
mode: options.mode || "auto",
|
|
6060
|
+
providerName: "glm",
|
|
6061
|
+
maxRetries: options.maxRetries ?? 1
|
|
6062
|
+
};
|
|
6063
|
+
super(baseOptions);
|
|
6064
|
+
this.model = options.model || GLM_DEFAULT_MODEL;
|
|
6065
|
+
this.sdkConfig = {
|
|
6066
|
+
apiKey: options.apiKey,
|
|
6067
|
+
baseUrl: options.baseUrl,
|
|
6068
|
+
timeout: options.timeout
|
|
6069
|
+
};
|
|
6070
|
+
this.cliConfig = {
|
|
6071
|
+
command: options.command,
|
|
6072
|
+
timeout: options.timeout
|
|
6073
|
+
};
|
|
6074
|
+
logger.debug("[GLM Hybrid] Adapter created", {
|
|
6075
|
+
mode: this.mode,
|
|
6076
|
+
model: this.model
|
|
6077
|
+
});
|
|
6078
|
+
}
|
|
6079
|
+
/**
|
|
6080
|
+
* Execute request via SDK
|
|
6081
|
+
*/
|
|
6082
|
+
async executeViaSDK(request) {
|
|
6083
|
+
if (!this.sdkAdapter) {
|
|
6084
|
+
throw new Error("GLM SDK adapter not initialized");
|
|
6085
|
+
}
|
|
6086
|
+
return this.sdkAdapter.execute(request);
|
|
6087
|
+
}
|
|
6088
|
+
/**
|
|
6089
|
+
* Execute request via CLI
|
|
6090
|
+
*/
|
|
6091
|
+
async executeViaCLI(request) {
|
|
6092
|
+
if (!this.cliWrapper) {
|
|
6093
|
+
throw new Error("GLM CLI wrapper not initialized");
|
|
6094
|
+
}
|
|
6095
|
+
return this.cliWrapper.execute(request);
|
|
6096
|
+
}
|
|
6097
|
+
/**
|
|
6098
|
+
* Check if SDK is available
|
|
6099
|
+
*/
|
|
6100
|
+
async isSDKAvailable() {
|
|
6101
|
+
try {
|
|
6102
|
+
const adapter = new GLMSdkAdapter({
|
|
6103
|
+
...this.sdkConfig,
|
|
6104
|
+
model: this.model
|
|
6105
|
+
});
|
|
6106
|
+
const available = await adapter.isAvailable();
|
|
6107
|
+
if (!available) {
|
|
6108
|
+
return false;
|
|
6109
|
+
}
|
|
6110
|
+
this.sdkAdapter = adapter;
|
|
6111
|
+
return true;
|
|
6112
|
+
} catch (error) {
|
|
6113
|
+
logger.debug("[GLM Hybrid] SDK availability check failed", {
|
|
6114
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6115
|
+
});
|
|
6116
|
+
return false;
|
|
6117
|
+
}
|
|
6118
|
+
}
|
|
6119
|
+
/**
|
|
6120
|
+
* Check if CLI is available
|
|
6121
|
+
*/
|
|
6122
|
+
async isCLIAvailable() {
|
|
6123
|
+
try {
|
|
6124
|
+
const wrapper = new GLMCliWrapper({
|
|
6125
|
+
...this.cliConfig,
|
|
6126
|
+
model: this.model
|
|
6127
|
+
});
|
|
6128
|
+
const available = await wrapper.isAvailable();
|
|
6129
|
+
if (!available) {
|
|
6130
|
+
return false;
|
|
6131
|
+
}
|
|
6132
|
+
this.cliWrapper = wrapper;
|
|
6133
|
+
return true;
|
|
6134
|
+
} catch (error) {
|
|
6135
|
+
logger.debug("[GLM Hybrid] CLI availability check failed", {
|
|
6136
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6137
|
+
});
|
|
6138
|
+
return false;
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
/**
|
|
6142
|
+
* Initialize SDK adapter
|
|
6143
|
+
*/
|
|
6144
|
+
async initializeSDK() {
|
|
6145
|
+
if (!this.sdkAdapter) {
|
|
6146
|
+
this.sdkAdapter = new GLMSdkAdapter({
|
|
6147
|
+
...this.sdkConfig,
|
|
6148
|
+
model: this.model
|
|
6149
|
+
});
|
|
6150
|
+
}
|
|
6151
|
+
await this.sdkAdapter.initialize();
|
|
6152
|
+
logger.debug("[GLM Hybrid] SDK initialized", {
|
|
6153
|
+
model: this.model
|
|
6154
|
+
});
|
|
6155
|
+
}
|
|
6156
|
+
/**
|
|
6157
|
+
* Initialize CLI wrapper
|
|
6158
|
+
*/
|
|
6159
|
+
async initializeCLI() {
|
|
6160
|
+
if (!this.cliWrapper) {
|
|
6161
|
+
this.cliWrapper = new GLMCliWrapper({
|
|
6162
|
+
...this.cliConfig,
|
|
6163
|
+
model: this.model
|
|
6164
|
+
});
|
|
6165
|
+
}
|
|
6166
|
+
await this.cliWrapper.initialize();
|
|
6167
|
+
logger.debug("[GLM Hybrid] CLI initialized", {
|
|
6168
|
+
model: this.model,
|
|
6169
|
+
version: this.cliWrapper.getVersion()
|
|
6170
|
+
});
|
|
6171
|
+
}
|
|
6172
|
+
/**
|
|
6173
|
+
* Clean up SDK resources
|
|
6174
|
+
*/
|
|
6175
|
+
async destroySDK() {
|
|
6176
|
+
if (this.sdkAdapter) {
|
|
6177
|
+
await this.sdkAdapter.destroy();
|
|
6178
|
+
this.sdkAdapter = null;
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
/**
|
|
6182
|
+
* Clean up CLI resources
|
|
6183
|
+
*/
|
|
6184
|
+
async destroyCLI() {
|
|
6185
|
+
if (this.cliWrapper) {
|
|
6186
|
+
await this.cliWrapper.destroy();
|
|
6187
|
+
this.cliWrapper = null;
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
/**
|
|
6191
|
+
* Get the configured model
|
|
6192
|
+
*/
|
|
6193
|
+
getModel() {
|
|
6194
|
+
return this.model;
|
|
6195
|
+
}
|
|
6196
|
+
/**
|
|
6197
|
+
* Get the CLI command being used
|
|
6198
|
+
*/
|
|
6199
|
+
getCommand() {
|
|
6200
|
+
return this.cliWrapper?.getCommand() || this.cliConfig.command || "ax-glm";
|
|
6201
|
+
}
|
|
6202
|
+
/**
|
|
6203
|
+
* Get CLI version
|
|
6204
|
+
*/
|
|
6205
|
+
getCLIVersion() {
|
|
6206
|
+
return this.cliWrapper?.getVersion() || null;
|
|
6207
|
+
}
|
|
6208
|
+
};
|
|
6209
|
+
}
|
|
6210
|
+
});
|
|
6211
|
+
|
|
6212
|
+
// src/integrations/ax-glm/sdk-only-adapter.ts
|
|
6213
|
+
var GLMSdkOnlyAdapter;
|
|
6214
|
+
var init_sdk_only_adapter = __esm({
|
|
6215
|
+
"src/integrations/ax-glm/sdk-only-adapter.ts"() {
|
|
6216
|
+
init_esm_shims();
|
|
6217
|
+
init_logger();
|
|
6218
|
+
init_sdk_adapter2();
|
|
6219
|
+
init_types2();
|
|
6220
|
+
GLMSdkOnlyAdapter = class {
|
|
6221
|
+
sdkAdapter;
|
|
6222
|
+
initialized = false;
|
|
6223
|
+
model;
|
|
6224
|
+
maxRetries;
|
|
6225
|
+
retryDelayMs;
|
|
6226
|
+
constructor(options = {}) {
|
|
6227
|
+
this.model = options.model || GLM_DEFAULT_MODEL;
|
|
6228
|
+
this.maxRetries = options.maxRetries ?? 2;
|
|
6229
|
+
this.retryDelayMs = options.retryDelayMs ?? 1e3;
|
|
6230
|
+
this.sdkAdapter = new GLMSdkAdapter({
|
|
6231
|
+
model: this.model,
|
|
6232
|
+
apiKey: options.apiKey,
|
|
6233
|
+
baseUrl: options.baseUrl,
|
|
6234
|
+
timeout: options.timeout
|
|
6235
|
+
});
|
|
6236
|
+
logger.debug("[GLM SDK-Only] Adapter created", {
|
|
6237
|
+
model: this.model,
|
|
6238
|
+
maxRetries: this.maxRetries
|
|
6239
|
+
});
|
|
6240
|
+
}
|
|
6241
|
+
/**
|
|
6242
|
+
* Check if SDK is available
|
|
6243
|
+
*/
|
|
6244
|
+
async isAvailable() {
|
|
6245
|
+
return this.sdkAdapter.isAvailable();
|
|
6246
|
+
}
|
|
6247
|
+
/**
|
|
6248
|
+
* Initialize the adapter
|
|
6249
|
+
*/
|
|
6250
|
+
async initialize() {
|
|
6251
|
+
if (this.initialized) {
|
|
6252
|
+
return;
|
|
6253
|
+
}
|
|
6254
|
+
const available = await this.sdkAdapter.isAvailable();
|
|
6255
|
+
if (!available) {
|
|
6256
|
+
throw new Error("GLM SDK not available - check API key and openai package");
|
|
6257
|
+
}
|
|
6258
|
+
await this.sdkAdapter.initialize();
|
|
6259
|
+
this.initialized = true;
|
|
6260
|
+
logger.debug("[GLM SDK-Only] Initialized", { model: this.model });
|
|
6261
|
+
}
|
|
6262
|
+
/**
|
|
6263
|
+
* Execute a request using GLM SDK
|
|
6264
|
+
*
|
|
6265
|
+
* Retries transient errors up to maxRetries times with exponential backoff.
|
|
6266
|
+
* Does NOT fall back to CLI - throws on persistent failure.
|
|
6267
|
+
*/
|
|
6268
|
+
async execute(request) {
|
|
6269
|
+
if (!this.initialized) {
|
|
6270
|
+
await this.initialize();
|
|
6271
|
+
}
|
|
6272
|
+
const startTime = Date.now();
|
|
6273
|
+
let lastError;
|
|
6274
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
6275
|
+
try {
|
|
6276
|
+
const response = await this.sdkAdapter.execute(request);
|
|
6277
|
+
logger.debug("[GLM SDK-Only] Execution succeeded", {
|
|
6278
|
+
model: this.model,
|
|
6279
|
+
attempt: attempt + 1,
|
|
6280
|
+
latencyMs: response.latencyMs
|
|
6281
|
+
});
|
|
6282
|
+
return response;
|
|
6283
|
+
} catch (error) {
|
|
6284
|
+
lastError = error;
|
|
6285
|
+
logger.warn("[GLM SDK-Only] Execution failed", {
|
|
6286
|
+
model: this.model,
|
|
6287
|
+
attempt: attempt + 1,
|
|
6288
|
+
maxRetries: this.maxRetries,
|
|
6289
|
+
error: lastError.message
|
|
6290
|
+
});
|
|
6291
|
+
if (attempt < this.maxRetries && this.isRetryableError(lastError)) {
|
|
6292
|
+
const delay = this.retryDelayMs * Math.pow(2, attempt);
|
|
6293
|
+
logger.debug("[GLM SDK-Only] Retrying after delay", {
|
|
6294
|
+
attempt: attempt + 1,
|
|
6295
|
+
delayMs: delay
|
|
6296
|
+
});
|
|
6297
|
+
await this.sleep(delay);
|
|
6298
|
+
continue;
|
|
6299
|
+
}
|
|
6300
|
+
break;
|
|
6301
|
+
}
|
|
6302
|
+
}
|
|
6303
|
+
const totalTime = Date.now() - startTime;
|
|
6304
|
+
logger.error("[GLM SDK-Only] All retries exhausted", {
|
|
6305
|
+
model: this.model,
|
|
6306
|
+
totalAttempts: this.maxRetries + 1,
|
|
6307
|
+
totalTimeMs: totalTime,
|
|
6308
|
+
lastError: lastError?.message
|
|
6309
|
+
});
|
|
6310
|
+
throw new Error(
|
|
6311
|
+
`GLM SDK execution failed after ${this.maxRetries + 1} attempts: ${lastError?.message || "Unknown error"}`
|
|
6312
|
+
);
|
|
6313
|
+
}
|
|
6314
|
+
/**
|
|
6315
|
+
* Check if error is retryable
|
|
6316
|
+
*/
|
|
6317
|
+
isRetryableError(error) {
|
|
6318
|
+
const message = error.message.toLowerCase();
|
|
6319
|
+
if (message.includes("rate limit") || message.includes("429")) {
|
|
6320
|
+
return true;
|
|
6321
|
+
}
|
|
6322
|
+
if (message.includes("500") || message.includes("502") || message.includes("503") || message.includes("504")) {
|
|
6323
|
+
return true;
|
|
6324
|
+
}
|
|
6325
|
+
if (message.includes("timeout") || message.includes("etimedout")) {
|
|
6326
|
+
return true;
|
|
6327
|
+
}
|
|
6328
|
+
if (message.includes("econnreset") || message.includes("econnrefused")) {
|
|
6329
|
+
return true;
|
|
6330
|
+
}
|
|
6331
|
+
return false;
|
|
6332
|
+
}
|
|
6333
|
+
/**
|
|
6334
|
+
* Sleep utility
|
|
6335
|
+
*/
|
|
6336
|
+
sleep(ms) {
|
|
6337
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
6338
|
+
}
|
|
6339
|
+
/**
|
|
6340
|
+
* Get the configured model
|
|
6341
|
+
*/
|
|
6342
|
+
getModel() {
|
|
6343
|
+
return this.model;
|
|
6344
|
+
}
|
|
6345
|
+
/**
|
|
6346
|
+
* Clean up resources
|
|
6347
|
+
*/
|
|
6348
|
+
async destroy() {
|
|
6349
|
+
await this.sdkAdapter.destroy();
|
|
6350
|
+
this.initialized = false;
|
|
6351
|
+
logger.debug("[GLM SDK-Only] Adapter destroyed");
|
|
6352
|
+
}
|
|
6353
|
+
};
|
|
6354
|
+
}
|
|
6355
|
+
});
|
|
6356
|
+
|
|
6357
|
+
// src/integrations/ax-glm/mcp-client-mode.ts
|
|
6358
|
+
var init_mcp_client_mode = __esm({
|
|
6359
|
+
"src/integrations/ax-glm/mcp-client-mode.ts"() {
|
|
6360
|
+
init_esm_shims();
|
|
6361
|
+
init_logger();
|
|
6362
|
+
init_sdk_adapter2();
|
|
6363
|
+
}
|
|
6364
|
+
});
|
|
6365
|
+
|
|
6366
|
+
// src/integrations/ax-glm/index.ts
|
|
6367
|
+
var init_ax_glm = __esm({
|
|
6368
|
+
"src/integrations/ax-glm/index.ts"() {
|
|
6369
|
+
init_esm_shims();
|
|
6370
|
+
init_hybrid_adapter2();
|
|
6371
|
+
init_sdk_adapter2();
|
|
6372
|
+
init_cli_wrapper2();
|
|
6373
|
+
init_sdk_only_adapter();
|
|
6374
|
+
init_mcp_client_mode();
|
|
6375
|
+
init_types2();
|
|
6376
|
+
}
|
|
6377
|
+
});
|
|
6378
|
+
|
|
6379
|
+
// src/providers/glm-provider.ts
|
|
6380
|
+
var glm_provider_exports = {};
|
|
6381
|
+
__export(glm_provider_exports, {
|
|
6382
|
+
GLMProvider: () => GLMProvider,
|
|
6383
|
+
default: () => GLMProvider
|
|
6384
|
+
});
|
|
6385
|
+
var MODEL_MAPPING, GLMProvider;
|
|
6386
|
+
var init_glm_provider = __esm({
|
|
6387
|
+
"src/providers/glm-provider.ts"() {
|
|
6388
|
+
init_esm_shims();
|
|
6389
|
+
init_base_provider();
|
|
6390
|
+
init_logger();
|
|
6391
|
+
init_ax_glm();
|
|
6392
|
+
MODEL_MAPPING = {
|
|
6393
|
+
"glm-4-plus": "glm-4.6",
|
|
6394
|
+
"glm-4v": "glm-4.5v",
|
|
6395
|
+
"glm-4-air": "glm-4-flash",
|
|
6396
|
+
"glm-4-airx": "glm-4-flash"
|
|
6397
|
+
};
|
|
6398
|
+
GLMProvider = class _GLMProvider extends BaseProvider {
|
|
6399
|
+
/** Selected model */
|
|
6400
|
+
model;
|
|
6401
|
+
/** SDK-only adapter for direct execution (v13.0.0) */
|
|
6402
|
+
sdkOnlyAdapter = null;
|
|
6403
|
+
/** Legacy hybrid adapter for 'auto' mode (backwards compatibility) */
|
|
6404
|
+
hybridAdapter = null;
|
|
6405
|
+
/** Provider configuration */
|
|
6406
|
+
glmConfig;
|
|
6407
|
+
/** Supported models */
|
|
6408
|
+
static SUPPORTED_MODELS = [
|
|
6409
|
+
"glm-4.6",
|
|
6410
|
+
"glm-4.5v",
|
|
6411
|
+
"glm-4",
|
|
6412
|
+
"glm-4-flash",
|
|
6413
|
+
// Legacy aliases
|
|
6414
|
+
"glm-4-plus",
|
|
6415
|
+
"glm-4v",
|
|
6416
|
+
"glm-4-air",
|
|
6417
|
+
"glm-4-airx"
|
|
6418
|
+
];
|
|
6419
|
+
constructor(config) {
|
|
6420
|
+
super({
|
|
6421
|
+
...config,
|
|
6422
|
+
command: "ax-glm"
|
|
6423
|
+
});
|
|
6424
|
+
this.glmConfig = config;
|
|
6425
|
+
const requestedModel = config.model || "glm-4";
|
|
6426
|
+
if (!_GLMProvider.SUPPORTED_MODELS.includes(requestedModel)) {
|
|
6427
|
+
logger.warn(`[GLM] Unknown model: ${requestedModel}. Using glm-4.`);
|
|
6428
|
+
this.model = "glm-4";
|
|
6429
|
+
} else {
|
|
6430
|
+
this.model = requestedModel;
|
|
6431
|
+
}
|
|
6432
|
+
logger.debug("[GLM Provider] Initialized", {
|
|
6433
|
+
model: this.model,
|
|
6434
|
+
mode: config.mode || "auto"
|
|
6435
|
+
});
|
|
6436
|
+
}
|
|
6437
|
+
/**
|
|
6438
|
+
* Get the normalized model name
|
|
6439
|
+
*/
|
|
6440
|
+
getNormalizedModel() {
|
|
6441
|
+
return MODEL_MAPPING[this.model] || this.model;
|
|
6442
|
+
}
|
|
6443
|
+
/**
|
|
6444
|
+
* Get or create SDK-only adapter (v13.0.0 default)
|
|
6445
|
+
*/
|
|
6446
|
+
getSdkOnlyAdapter() {
|
|
6447
|
+
if (!this.sdkOnlyAdapter) {
|
|
6448
|
+
this.sdkOnlyAdapter = new GLMSdkOnlyAdapter({
|
|
6449
|
+
model: this.model,
|
|
6450
|
+
apiKey: this.glmConfig.apiKey,
|
|
6451
|
+
baseUrl: this.glmConfig.baseUrl,
|
|
6452
|
+
timeout: this.glmConfig.timeout
|
|
6453
|
+
});
|
|
6454
|
+
}
|
|
6455
|
+
return this.sdkOnlyAdapter;
|
|
6456
|
+
}
|
|
6457
|
+
/**
|
|
6458
|
+
* Get or create hybrid adapter (legacy, for 'auto' mode only)
|
|
6459
|
+
*/
|
|
6460
|
+
getHybridAdapter() {
|
|
6461
|
+
if (!this.hybridAdapter) {
|
|
6462
|
+
const options = {
|
|
6463
|
+
mode: this.glmConfig.mode || "auto",
|
|
6464
|
+
model: this.model,
|
|
6465
|
+
apiKey: this.glmConfig.apiKey,
|
|
6466
|
+
baseUrl: this.glmConfig.baseUrl,
|
|
6467
|
+
command: "ax-glm",
|
|
6468
|
+
timeout: this.glmConfig.timeout
|
|
6469
|
+
};
|
|
6470
|
+
this.hybridAdapter = new GLMHybridAdapter(options);
|
|
6471
|
+
}
|
|
6472
|
+
return this.hybridAdapter;
|
|
6473
|
+
}
|
|
6474
|
+
/**
|
|
6475
|
+
* Execute a task using GLM
|
|
6476
|
+
*
|
|
6477
|
+
* Execution flow (v13.0.0):
|
|
6478
|
+
* 1. Mock mode → return mock response
|
|
6479
|
+
* 2. mode='sdk' (default) → use SDK-only adapter (NO CLI fallback)
|
|
6480
|
+
* 3. mode='auto' (legacy) → use hybrid adapter (SDK with CLI fallback)
|
|
6481
|
+
* 4. mode='cli' → use CLI via BaseProvider (deprecated for GLM)
|
|
6482
|
+
*/
|
|
6483
|
+
async execute(request) {
|
|
6484
|
+
if (process.env.AX_MOCK_PROVIDERS === "true") {
|
|
6485
|
+
return this.createMockResponse(request.prompt);
|
|
6486
|
+
}
|
|
6487
|
+
const effectiveMode = this.glmConfig.mode || "sdk";
|
|
6488
|
+
if (effectiveMode === "cli") {
|
|
6489
|
+
logger.warn("[GLM Provider] CLI mode is deprecated for GLM. Consider using SDK mode.", {
|
|
6490
|
+
model: this.model
|
|
6491
|
+
});
|
|
6492
|
+
return super.execute(request);
|
|
6493
|
+
}
|
|
6494
|
+
if (effectiveMode === "auto") {
|
|
6495
|
+
logger.debug("[GLM Provider] Executing via hybrid adapter (legacy auto mode)", {
|
|
6496
|
+
promptLength: request.prompt.length,
|
|
6497
|
+
model: this.model
|
|
6498
|
+
});
|
|
6499
|
+
const adapter2 = this.getHybridAdapter();
|
|
6500
|
+
return adapter2.execute(request);
|
|
6501
|
+
}
|
|
6502
|
+
logger.debug("[GLM Provider] Executing via SDK-only adapter", {
|
|
6503
|
+
promptLength: request.prompt.length,
|
|
6504
|
+
model: this.model
|
|
6505
|
+
});
|
|
6506
|
+
const adapter = this.getSdkOnlyAdapter();
|
|
6507
|
+
return adapter.execute(request);
|
|
6508
|
+
}
|
|
6509
|
+
/**
|
|
6510
|
+
* Get CLI command
|
|
6511
|
+
*/
|
|
6512
|
+
getCLICommand() {
|
|
6513
|
+
const adapter = this.hybridAdapter;
|
|
6514
|
+
if (adapter) {
|
|
6515
|
+
const activeMode = adapter.getActiveMode();
|
|
6516
|
+
if (activeMode === "sdk") {
|
|
6517
|
+
return "glm-sdk";
|
|
6518
|
+
}
|
|
6519
|
+
}
|
|
6520
|
+
return "ax-glm";
|
|
6521
|
+
}
|
|
6522
|
+
/**
|
|
6523
|
+
* Get CLI arguments for ax-glm headless mode
|
|
6524
|
+
*/
|
|
6525
|
+
getCLIArgs() {
|
|
6526
|
+
const args2 = ["-p"];
|
|
6527
|
+
const normalizedModel = this.getNormalizedModel();
|
|
6528
|
+
if (normalizedModel !== "glm-4") {
|
|
6529
|
+
args2.push("--model", normalizedModel);
|
|
6530
|
+
}
|
|
6531
|
+
return args2;
|
|
6532
|
+
}
|
|
6533
|
+
/**
|
|
6534
|
+
* Create mock response for testing
|
|
6535
|
+
*/
|
|
6536
|
+
createMockResponse(prompt) {
|
|
6537
|
+
return {
|
|
6538
|
+
content: this.getMockResponse(),
|
|
6539
|
+
model: this.getNormalizedModel(),
|
|
6540
|
+
tokensUsed: {
|
|
6541
|
+
prompt: this.estimateTokens(prompt),
|
|
6542
|
+
completion: 50,
|
|
6543
|
+
total: this.estimateTokens(prompt) + 50
|
|
6544
|
+
},
|
|
6545
|
+
latencyMs: 10,
|
|
6546
|
+
finishReason: "stop",
|
|
6547
|
+
cached: false
|
|
6548
|
+
};
|
|
6549
|
+
}
|
|
6550
|
+
/**
|
|
6551
|
+
* Estimate token count
|
|
6552
|
+
*/
|
|
6553
|
+
estimateTokens(text) {
|
|
6554
|
+
return Math.ceil(text.length / 4);
|
|
6555
|
+
}
|
|
6556
|
+
/**
|
|
6557
|
+
* Get mock response for testing
|
|
6558
|
+
*/
|
|
6559
|
+
getMockResponse() {
|
|
6560
|
+
return `[Mock GLM Response]
|
|
6561
|
+
|
|
6562
|
+
This is a mock response from the GLM provider (${this.getNormalizedModel()}).
|
|
6563
|
+
In production, this would be a response from ${this.glmConfig.mode === "sdk" ? "GLM SDK" : "ax-glm CLI"}.
|
|
6564
|
+
|
|
6565
|
+
Model: ${this.getNormalizedModel()}
|
|
6566
|
+
Provider: GLM (Zhipu AI)
|
|
6567
|
+
Mode: ${this.glmConfig.mode || "auto"}`;
|
|
6568
|
+
}
|
|
6569
|
+
/**
|
|
6570
|
+
* Get provider capabilities
|
|
6571
|
+
*/
|
|
6572
|
+
get capabilities() {
|
|
6573
|
+
const model = this.getNormalizedModel();
|
|
6574
|
+
const isVision = model.includes("v") || model === "glm-4.5v";
|
|
6575
|
+
let maxContextTokens = 128e3;
|
|
6576
|
+
if (model === "glm-4.6") maxContextTokens = 2e5;
|
|
6577
|
+
if (model === "glm-4.5v") maxContextTokens = 64e3;
|
|
6578
|
+
const activeMode = this.hybridAdapter?.getActiveMode();
|
|
6579
|
+
const integrationMode = activeMode === "sdk" ? "sdk" : "cli";
|
|
6580
|
+
return {
|
|
6581
|
+
...super.capabilities,
|
|
6582
|
+
supportsStreaming: true,
|
|
6583
|
+
supportsVision: isVision,
|
|
6584
|
+
maxContextTokens,
|
|
6585
|
+
supportedModels: _GLMProvider.SUPPORTED_MODELS,
|
|
6586
|
+
integrationMode
|
|
6587
|
+
};
|
|
6588
|
+
}
|
|
6589
|
+
/**
|
|
6590
|
+
* Get the active execution mode
|
|
6591
|
+
*/
|
|
6592
|
+
getActiveMode() {
|
|
6593
|
+
return this.hybridAdapter?.getActiveMode() || null;
|
|
6594
|
+
}
|
|
6595
|
+
/**
|
|
6596
|
+
* Reset circuit breakers
|
|
6597
|
+
*/
|
|
6598
|
+
resetCircuitBreakers() {
|
|
6599
|
+
this.hybridAdapter?.resetCircuitBreakers();
|
|
6600
|
+
}
|
|
6601
|
+
/**
|
|
6602
|
+
* Clean up resources
|
|
6603
|
+
*/
|
|
6604
|
+
async destroy() {
|
|
6605
|
+
if (this.sdkOnlyAdapter) {
|
|
6606
|
+
await this.sdkOnlyAdapter.destroy();
|
|
6607
|
+
this.sdkOnlyAdapter = null;
|
|
6608
|
+
}
|
|
6609
|
+
if (this.hybridAdapter) {
|
|
6610
|
+
await this.hybridAdapter.destroy();
|
|
6611
|
+
this.hybridAdapter = null;
|
|
6612
|
+
}
|
|
6613
|
+
}
|
|
6614
|
+
/**
|
|
6615
|
+
* Get the list of supported models
|
|
6616
|
+
*/
|
|
6617
|
+
static getSupportedModels() {
|
|
6618
|
+
return [..._GLMProvider.SUPPORTED_MODELS];
|
|
6619
|
+
}
|
|
6620
|
+
};
|
|
6621
|
+
}
|
|
6622
|
+
});
|
|
6623
|
+
|
|
6624
|
+
// src/integrations/ax-grok/types.ts
|
|
6625
|
+
function normalizeGrokModel(model) {
|
|
6626
|
+
return GROK_MODEL_MAPPING[model] || model;
|
|
6627
|
+
}
|
|
6628
|
+
var GROK_MODEL_MAPPING, GROK_DEFAULT_BASE_URL, GROK_DEFAULT_MODEL, GROK_DEFAULT_COMMAND;
|
|
6629
|
+
var init_types3 = __esm({
|
|
6630
|
+
"src/integrations/ax-grok/types.ts"() {
|
|
6631
|
+
init_esm_shims();
|
|
6632
|
+
GROK_MODEL_MAPPING = {
|
|
6633
|
+
"grok-beta": "grok-3"
|
|
6634
|
+
};
|
|
6635
|
+
GROK_DEFAULT_BASE_URL = "https://api.x.ai/v1";
|
|
6636
|
+
GROK_DEFAULT_MODEL = "grok-3";
|
|
6637
|
+
GROK_DEFAULT_COMMAND = "ax-grok";
|
|
6638
|
+
}
|
|
6639
|
+
});
|
|
6640
|
+
|
|
6641
|
+
// src/integrations/ax-grok/sdk-adapter.ts
|
|
6642
|
+
var GrokSdkAdapter;
|
|
6643
|
+
var init_sdk_adapter3 = __esm({
|
|
6644
|
+
"src/integrations/ax-grok/sdk-adapter.ts"() {
|
|
6645
|
+
init_esm_shims();
|
|
6646
|
+
init_logger();
|
|
6647
|
+
init_validation_limits();
|
|
6648
|
+
init_types3();
|
|
6649
|
+
GrokSdkAdapter = class {
|
|
6650
|
+
client = null;
|
|
6651
|
+
config;
|
|
6652
|
+
initialized = false;
|
|
6653
|
+
constructor(config = {}) {
|
|
6654
|
+
this.config = {
|
|
6655
|
+
apiKey: config.apiKey || process.env.XAI_API_KEY || "",
|
|
6656
|
+
baseUrl: config.baseUrl || GROK_DEFAULT_BASE_URL,
|
|
6657
|
+
model: config.model || GROK_DEFAULT_MODEL,
|
|
6658
|
+
timeout: config.timeout || TIMEOUTS.PROVIDER_DEFAULT
|
|
6659
|
+
};
|
|
6660
|
+
logger.debug("[Grok SDK] Adapter created", {
|
|
6661
|
+
model: this.config.model,
|
|
6662
|
+
baseUrl: this.config.baseUrl,
|
|
6663
|
+
hasApiKey: !!this.config.apiKey
|
|
6664
|
+
});
|
|
6665
|
+
}
|
|
6666
|
+
/**
|
|
6667
|
+
* Check if SDK is available (OpenAI package installed and API key present)
|
|
6668
|
+
*/
|
|
6669
|
+
async isAvailable() {
|
|
6670
|
+
try {
|
|
6671
|
+
if (!this.config.apiKey) {
|
|
6672
|
+
logger.debug("[Grok SDK] No API key configured");
|
|
6673
|
+
return false;
|
|
6674
|
+
}
|
|
6675
|
+
await import('openai');
|
|
6676
|
+
return true;
|
|
6677
|
+
} catch (error) {
|
|
6678
|
+
logger.debug("[Grok SDK] OpenAI SDK not available", {
|
|
6679
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6680
|
+
});
|
|
6681
|
+
return false;
|
|
6682
|
+
}
|
|
6683
|
+
}
|
|
6684
|
+
/**
|
|
6685
|
+
* Initialize the SDK client
|
|
6686
|
+
*/
|
|
6687
|
+
async initialize() {
|
|
6688
|
+
if (this.initialized) {
|
|
6689
|
+
return;
|
|
6690
|
+
}
|
|
6691
|
+
try {
|
|
6692
|
+
const OpenAI = (await import('openai')).default;
|
|
6693
|
+
this.client = new OpenAI({
|
|
6694
|
+
apiKey: this.config.apiKey,
|
|
6695
|
+
baseURL: this.config.baseUrl,
|
|
6696
|
+
timeout: this.config.timeout
|
|
6697
|
+
});
|
|
6698
|
+
this.initialized = true;
|
|
6699
|
+
logger.debug("[Grok SDK] Client initialized", {
|
|
6700
|
+
model: this.config.model
|
|
6701
|
+
});
|
|
6702
|
+
} catch (error) {
|
|
6703
|
+
throw new Error(
|
|
6704
|
+
`Failed to initialize Grok SDK: ${error instanceof Error ? error.message : String(error)}`
|
|
6705
|
+
);
|
|
6706
|
+
}
|
|
6707
|
+
}
|
|
6708
|
+
/**
|
|
6709
|
+
* Execute a request using the Grok SDK
|
|
6710
|
+
*/
|
|
6711
|
+
async execute(request) {
|
|
6712
|
+
if (!this.initialized) {
|
|
6713
|
+
await this.initialize();
|
|
6714
|
+
}
|
|
6715
|
+
const startTime = Date.now();
|
|
6716
|
+
try {
|
|
6717
|
+
const messages = [];
|
|
6718
|
+
if (request.systemPrompt) {
|
|
6719
|
+
messages.push({
|
|
6720
|
+
role: "system",
|
|
6721
|
+
content: request.systemPrompt
|
|
6722
|
+
});
|
|
6723
|
+
}
|
|
6724
|
+
messages.push({
|
|
6725
|
+
role: "user",
|
|
6726
|
+
content: request.prompt
|
|
6727
|
+
});
|
|
6728
|
+
const model = normalizeGrokModel(this.config.model);
|
|
6729
|
+
logger.debug("[Grok SDK] Executing request", {
|
|
6730
|
+
model,
|
|
6731
|
+
messageCount: messages.length,
|
|
6732
|
+
promptLength: request.prompt.length
|
|
6733
|
+
});
|
|
6734
|
+
const openaiClient = this.client;
|
|
6735
|
+
const response = await openaiClient.chat.completions.create({
|
|
6736
|
+
model,
|
|
6737
|
+
messages,
|
|
6738
|
+
max_tokens: request.maxTokens,
|
|
6739
|
+
temperature: request.temperature,
|
|
6740
|
+
stream: false
|
|
6741
|
+
});
|
|
6742
|
+
const latencyMs = Date.now() - startTime;
|
|
6743
|
+
if (!response.choices || response.choices.length === 0) {
|
|
6744
|
+
throw new Error("Grok API returned empty choices array");
|
|
6745
|
+
}
|
|
6746
|
+
const choice = response.choices[0];
|
|
6747
|
+
const content = choice?.message?.content || "";
|
|
6748
|
+
const finishReason = choice?.finish_reason || "unknown";
|
|
6749
|
+
logger.debug("[Grok SDK] Request completed", {
|
|
6750
|
+
model: response.model,
|
|
6751
|
+
latencyMs,
|
|
6752
|
+
tokensUsed: response.usage?.total_tokens
|
|
6753
|
+
});
|
|
6754
|
+
return {
|
|
6755
|
+
content,
|
|
6756
|
+
model: response.model,
|
|
6757
|
+
tokensUsed: response.usage ? {
|
|
6758
|
+
prompt: response.usage.prompt_tokens,
|
|
6759
|
+
completion: response.usage.completion_tokens,
|
|
6760
|
+
total: response.usage.total_tokens
|
|
6761
|
+
} : { prompt: 0, completion: 0, total: 0 },
|
|
6762
|
+
latencyMs,
|
|
6763
|
+
finishReason,
|
|
6764
|
+
cached: false
|
|
6765
|
+
};
|
|
6766
|
+
} catch (error) {
|
|
6767
|
+
const latencyMs = Date.now() - startTime;
|
|
6768
|
+
logger.error("[Grok SDK] Request failed", {
|
|
6769
|
+
error: error instanceof Error ? error.message : String(error),
|
|
6770
|
+
latencyMs
|
|
6771
|
+
});
|
|
6772
|
+
throw error;
|
|
6773
|
+
}
|
|
6774
|
+
}
|
|
6775
|
+
/**
|
|
6776
|
+
* Get the configured model
|
|
6777
|
+
*/
|
|
6778
|
+
getModel() {
|
|
6779
|
+
return this.config.model;
|
|
6780
|
+
}
|
|
6781
|
+
/**
|
|
6782
|
+
* Clean up resources
|
|
6783
|
+
*/
|
|
6784
|
+
async destroy() {
|
|
6785
|
+
this.client = null;
|
|
6786
|
+
this.initialized = false;
|
|
6787
|
+
logger.debug("[Grok SDK] Adapter destroyed");
|
|
6788
|
+
}
|
|
6789
|
+
};
|
|
6790
|
+
}
|
|
6791
|
+
});
|
|
6792
|
+
var execAsync2, GrokCliWrapper;
|
|
6793
|
+
var init_cli_wrapper3 = __esm({
|
|
6794
|
+
"src/integrations/ax-grok/cli-wrapper.ts"() {
|
|
6795
|
+
init_esm_shims();
|
|
6796
|
+
init_logger();
|
|
6797
|
+
init_validation_limits();
|
|
6798
|
+
init_types3();
|
|
6799
|
+
execAsync2 = promisify(exec);
|
|
6800
|
+
GrokCliWrapper = class {
|
|
6801
|
+
config;
|
|
6802
|
+
cliPath = null;
|
|
6803
|
+
cliVersion = null;
|
|
6804
|
+
constructor(config = {}) {
|
|
6805
|
+
this.config = {
|
|
6806
|
+
command: config.command || GROK_DEFAULT_COMMAND,
|
|
6807
|
+
model: config.model || GROK_DEFAULT_MODEL,
|
|
6808
|
+
timeout: config.timeout || TIMEOUTS.PROVIDER_DEFAULT
|
|
6809
|
+
};
|
|
6810
|
+
logger.debug("[Grok CLI] Wrapper created", {
|
|
6811
|
+
command: this.config.command,
|
|
6812
|
+
model: this.config.model
|
|
6813
|
+
});
|
|
6814
|
+
}
|
|
6815
|
+
/**
|
|
6816
|
+
* Check if CLI is available
|
|
6817
|
+
*/
|
|
6818
|
+
async isAvailable() {
|
|
6819
|
+
try {
|
|
6820
|
+
const { stdout } = await execAsync2(`which ${this.config.command}`, {
|
|
6821
|
+
timeout: TIMEOUTS.PROVIDER_DETECTION
|
|
6822
|
+
});
|
|
6823
|
+
this.cliPath = stdout.trim();
|
|
6824
|
+
if (this.cliPath) {
|
|
6825
|
+
try {
|
|
6826
|
+
const { stdout: versionOutput } = await execAsync2(
|
|
6827
|
+
`${this.config.command} --version`,
|
|
6828
|
+
{ timeout: TIMEOUTS.PROVIDER_DETECTION }
|
|
6829
|
+
);
|
|
6830
|
+
this.cliVersion = versionOutput.trim();
|
|
6831
|
+
} catch {
|
|
6832
|
+
this.cliVersion = "unknown";
|
|
6833
|
+
}
|
|
6834
|
+
logger.debug("[Grok CLI] CLI available", {
|
|
6835
|
+
path: this.cliPath,
|
|
6836
|
+
version: this.cliVersion
|
|
6837
|
+
});
|
|
6838
|
+
return true;
|
|
6839
|
+
}
|
|
6840
|
+
return false;
|
|
6841
|
+
} catch (error) {
|
|
6842
|
+
logger.debug("[Grok CLI] CLI not available", {
|
|
6843
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6844
|
+
});
|
|
6845
|
+
return false;
|
|
6846
|
+
}
|
|
6847
|
+
}
|
|
6848
|
+
/**
|
|
6849
|
+
* Initialize the CLI wrapper
|
|
6850
|
+
*/
|
|
6851
|
+
async initialize() {
|
|
6852
|
+
const available = await this.isAvailable();
|
|
6853
|
+
if (!available) {
|
|
6854
|
+
throw new Error(`${this.config.command} CLI is not installed or not in PATH`);
|
|
6855
|
+
}
|
|
6856
|
+
}
|
|
6857
|
+
/**
|
|
6858
|
+
* Execute a request using the CLI
|
|
6859
|
+
*/
|
|
6860
|
+
async execute(request) {
|
|
6861
|
+
const startTime = Date.now();
|
|
6862
|
+
try {
|
|
6863
|
+
const args2 = this.buildArgs(request);
|
|
6864
|
+
logger.debug("[Grok CLI] Executing", {
|
|
6865
|
+
command: this.config.command,
|
|
6866
|
+
args: args2,
|
|
6867
|
+
promptLength: request.prompt.length
|
|
6868
|
+
});
|
|
6869
|
+
const result = await this.spawnCLI(args2, request.prompt);
|
|
6870
|
+
const latencyMs = Date.now() - startTime;
|
|
6871
|
+
const response = this.parseResponse(result);
|
|
6872
|
+
logger.debug("[Grok CLI] Request completed", {
|
|
6873
|
+
latencyMs,
|
|
6874
|
+
contentLength: response.content.length
|
|
6875
|
+
});
|
|
6876
|
+
return {
|
|
6877
|
+
...response,
|
|
6878
|
+
latencyMs,
|
|
6879
|
+
cached: false
|
|
6880
|
+
};
|
|
6881
|
+
} catch (error) {
|
|
6882
|
+
const latencyMs = Date.now() - startTime;
|
|
6883
|
+
logger.error("[Grok CLI] Request failed", {
|
|
6884
|
+
error: error instanceof Error ? error.message : String(error),
|
|
6885
|
+
latencyMs
|
|
6886
|
+
});
|
|
6887
|
+
throw error;
|
|
6888
|
+
}
|
|
6889
|
+
}
|
|
6890
|
+
/**
|
|
6891
|
+
* Build CLI arguments
|
|
6892
|
+
*/
|
|
6893
|
+
buildArgs(request) {
|
|
6894
|
+
const args2 = ["-p"];
|
|
6895
|
+
const model = normalizeGrokModel(this.config.model);
|
|
6896
|
+
if (model !== "grok-3") {
|
|
6897
|
+
args2.push("--model", model);
|
|
6898
|
+
}
|
|
6899
|
+
if (request.systemPrompt) {
|
|
6900
|
+
args2.push("--system", request.systemPrompt);
|
|
6901
|
+
}
|
|
6902
|
+
if (request.maxTokens) {
|
|
6903
|
+
args2.push("--max-tokens", String(request.maxTokens));
|
|
6904
|
+
}
|
|
6905
|
+
if (request.temperature !== void 0) {
|
|
6906
|
+
args2.push("--temperature", String(request.temperature));
|
|
6907
|
+
}
|
|
6908
|
+
return args2;
|
|
6909
|
+
}
|
|
6910
|
+
/**
|
|
6911
|
+
* Spawn CLI process and get output
|
|
6912
|
+
*/
|
|
6913
|
+
spawnCLI(args2, prompt) {
|
|
6914
|
+
return new Promise((resolve5, reject) => {
|
|
6915
|
+
const process2 = spawn(this.config.command, args2, {
|
|
6916
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
6917
|
+
timeout: this.config.timeout
|
|
6918
|
+
});
|
|
6919
|
+
let stdout = "";
|
|
6920
|
+
let stderr = "";
|
|
6921
|
+
process2.stdout.on("data", (data) => {
|
|
6922
|
+
stdout += data.toString();
|
|
6923
|
+
});
|
|
6924
|
+
process2.stderr.on("data", (data) => {
|
|
6925
|
+
stderr += data.toString();
|
|
6926
|
+
});
|
|
6927
|
+
process2.on("error", (error) => {
|
|
6928
|
+
reject(new Error(`CLI process error: ${error.message}`));
|
|
6929
|
+
});
|
|
6930
|
+
process2.on("close", (code) => {
|
|
6931
|
+
if (code === 0) {
|
|
6932
|
+
resolve5(stdout);
|
|
6933
|
+
} else {
|
|
6934
|
+
reject(new Error(
|
|
6935
|
+
`CLI exited with code ${code}: ${stderr || "No error message"}`
|
|
6936
|
+
));
|
|
6937
|
+
}
|
|
6938
|
+
});
|
|
6939
|
+
process2.stdin.write(prompt);
|
|
6940
|
+
process2.stdin.end();
|
|
6941
|
+
});
|
|
6942
|
+
}
|
|
6943
|
+
/**
|
|
6944
|
+
* Parse CLI response
|
|
6945
|
+
*/
|
|
6946
|
+
parseResponse(output) {
|
|
6947
|
+
try {
|
|
6948
|
+
const parsed = JSON.parse(output.trim());
|
|
6949
|
+
if (parsed.content || parsed.message) {
|
|
6950
|
+
return {
|
|
6951
|
+
content: parsed.content || parsed.message || "",
|
|
6952
|
+
model: parsed.model || normalizeGrokModel(this.config.model),
|
|
6953
|
+
tokensUsed: parsed.usage ? {
|
|
6954
|
+
prompt: parsed.usage.prompt_tokens || 0,
|
|
6955
|
+
completion: parsed.usage.completion_tokens || 0,
|
|
6956
|
+
total: parsed.usage.total_tokens || 0
|
|
6957
|
+
} : { prompt: 0, completion: 0, total: 0 },
|
|
6958
|
+
finishReason: parsed.finish_reason || "stop"
|
|
6959
|
+
};
|
|
6960
|
+
}
|
|
6961
|
+
} catch {
|
|
6962
|
+
}
|
|
6963
|
+
return {
|
|
6964
|
+
content: output.trim(),
|
|
6965
|
+
model: normalizeGrokModel(this.config.model),
|
|
6966
|
+
tokensUsed: { prompt: 0, completion: 0, total: 0 },
|
|
6967
|
+
finishReason: "stop"
|
|
6968
|
+
};
|
|
6969
|
+
}
|
|
6970
|
+
/**
|
|
6971
|
+
* Get the configured model
|
|
6972
|
+
*/
|
|
6973
|
+
getModel() {
|
|
6974
|
+
return this.config.model;
|
|
6975
|
+
}
|
|
6976
|
+
/**
|
|
6977
|
+
* Get CLI version
|
|
6978
|
+
*/
|
|
6979
|
+
getVersion() {
|
|
6980
|
+
return this.cliVersion;
|
|
6981
|
+
}
|
|
6982
|
+
/**
|
|
6983
|
+
* Get CLI command
|
|
6984
|
+
*/
|
|
6985
|
+
getCommand() {
|
|
6986
|
+
return this.config.command;
|
|
6987
|
+
}
|
|
6988
|
+
/**
|
|
6989
|
+
* Clean up resources
|
|
6990
|
+
*/
|
|
6991
|
+
async destroy() {
|
|
6992
|
+
logger.debug("[Grok CLI] Wrapper destroyed");
|
|
6993
|
+
}
|
|
6994
|
+
};
|
|
6995
|
+
}
|
|
6996
|
+
});
|
|
6997
|
+
|
|
6998
|
+
// src/integrations/ax-grok/hybrid-adapter.ts
|
|
6999
|
+
var GrokHybridAdapter;
|
|
7000
|
+
var init_hybrid_adapter3 = __esm({
|
|
7001
|
+
"src/integrations/ax-grok/hybrid-adapter.ts"() {
|
|
7002
|
+
init_esm_shims();
|
|
7003
|
+
init_hybrid_adapter_base();
|
|
7004
|
+
init_sdk_adapter3();
|
|
7005
|
+
init_cli_wrapper3();
|
|
7006
|
+
init_types3();
|
|
7007
|
+
init_logger();
|
|
7008
|
+
GrokHybridAdapter = class extends HybridAdapterBase {
|
|
7009
|
+
sdkAdapter = null;
|
|
7010
|
+
cliWrapper = null;
|
|
7011
|
+
model;
|
|
7012
|
+
sdkConfig;
|
|
7013
|
+
cliConfig;
|
|
7014
|
+
constructor(options = {}) {
|
|
7015
|
+
const baseOptions = {
|
|
7016
|
+
mode: options.mode || "auto",
|
|
7017
|
+
providerName: "grok",
|
|
7018
|
+
maxRetries: options.maxRetries ?? 1
|
|
7019
|
+
};
|
|
7020
|
+
super(baseOptions);
|
|
7021
|
+
this.model = options.model || GROK_DEFAULT_MODEL;
|
|
7022
|
+
this.sdkConfig = {
|
|
7023
|
+
apiKey: options.apiKey,
|
|
7024
|
+
baseUrl: options.baseUrl,
|
|
7025
|
+
timeout: options.timeout
|
|
7026
|
+
};
|
|
7027
|
+
this.cliConfig = {
|
|
7028
|
+
command: options.command,
|
|
7029
|
+
timeout: options.timeout
|
|
7030
|
+
};
|
|
7031
|
+
logger.debug("[Grok Hybrid] Adapter created", {
|
|
7032
|
+
mode: this.mode,
|
|
7033
|
+
model: this.model
|
|
7034
|
+
});
|
|
7035
|
+
}
|
|
7036
|
+
/**
|
|
7037
|
+
* Execute request via SDK
|
|
7038
|
+
*/
|
|
7039
|
+
async executeViaSDK(request) {
|
|
7040
|
+
if (!this.sdkAdapter) {
|
|
7041
|
+
throw new Error("Grok SDK adapter not initialized");
|
|
7042
|
+
}
|
|
7043
|
+
return this.sdkAdapter.execute(request);
|
|
7044
|
+
}
|
|
7045
|
+
/**
|
|
7046
|
+
* Execute request via CLI
|
|
7047
|
+
*/
|
|
7048
|
+
async executeViaCLI(request) {
|
|
7049
|
+
if (!this.cliWrapper) {
|
|
7050
|
+
throw new Error("Grok CLI wrapper not initialized");
|
|
7051
|
+
}
|
|
7052
|
+
return this.cliWrapper.execute(request);
|
|
7053
|
+
}
|
|
7054
|
+
/**
|
|
7055
|
+
* Check if SDK is available
|
|
7056
|
+
*/
|
|
7057
|
+
async isSDKAvailable() {
|
|
7058
|
+
try {
|
|
7059
|
+
const adapter = new GrokSdkAdapter({
|
|
7060
|
+
...this.sdkConfig,
|
|
7061
|
+
model: this.model
|
|
7062
|
+
});
|
|
7063
|
+
const available = await adapter.isAvailable();
|
|
7064
|
+
if (!available) {
|
|
7065
|
+
return false;
|
|
7066
|
+
}
|
|
7067
|
+
this.sdkAdapter = adapter;
|
|
7068
|
+
return true;
|
|
7069
|
+
} catch (error) {
|
|
7070
|
+
logger.debug("[Grok Hybrid] SDK availability check failed", {
|
|
7071
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7072
|
+
});
|
|
7073
|
+
return false;
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
7076
|
+
/**
|
|
7077
|
+
* Check if CLI is available
|
|
7078
|
+
*/
|
|
7079
|
+
async isCLIAvailable() {
|
|
7080
|
+
try {
|
|
7081
|
+
const wrapper = new GrokCliWrapper({
|
|
7082
|
+
...this.cliConfig,
|
|
7083
|
+
model: this.model
|
|
7084
|
+
});
|
|
7085
|
+
const available = await wrapper.isAvailable();
|
|
7086
|
+
if (!available) {
|
|
7087
|
+
return false;
|
|
7088
|
+
}
|
|
7089
|
+
this.cliWrapper = wrapper;
|
|
7090
|
+
return true;
|
|
7091
|
+
} catch (error) {
|
|
7092
|
+
logger.debug("[Grok Hybrid] CLI availability check failed", {
|
|
7093
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7094
|
+
});
|
|
7095
|
+
return false;
|
|
7096
|
+
}
|
|
7097
|
+
}
|
|
7098
|
+
/**
|
|
7099
|
+
* Initialize SDK adapter
|
|
7100
|
+
*/
|
|
7101
|
+
async initializeSDK() {
|
|
7102
|
+
if (!this.sdkAdapter) {
|
|
7103
|
+
this.sdkAdapter = new GrokSdkAdapter({
|
|
7104
|
+
...this.sdkConfig,
|
|
7105
|
+
model: this.model
|
|
7106
|
+
});
|
|
7107
|
+
}
|
|
7108
|
+
await this.sdkAdapter.initialize();
|
|
7109
|
+
logger.debug("[Grok Hybrid] SDK initialized", {
|
|
7110
|
+
model: this.model
|
|
7111
|
+
});
|
|
7112
|
+
}
|
|
7113
|
+
/**
|
|
7114
|
+
* Initialize CLI wrapper
|
|
7115
|
+
*/
|
|
7116
|
+
async initializeCLI() {
|
|
7117
|
+
if (!this.cliWrapper) {
|
|
7118
|
+
this.cliWrapper = new GrokCliWrapper({
|
|
7119
|
+
...this.cliConfig,
|
|
7120
|
+
model: this.model
|
|
7121
|
+
});
|
|
7122
|
+
}
|
|
7123
|
+
await this.cliWrapper.initialize();
|
|
7124
|
+
logger.debug("[Grok Hybrid] CLI initialized", {
|
|
7125
|
+
model: this.model,
|
|
7126
|
+
version: this.cliWrapper.getVersion()
|
|
7127
|
+
});
|
|
7128
|
+
}
|
|
7129
|
+
/**
|
|
7130
|
+
* Clean up SDK resources
|
|
7131
|
+
*/
|
|
7132
|
+
async destroySDK() {
|
|
7133
|
+
if (this.sdkAdapter) {
|
|
7134
|
+
await this.sdkAdapter.destroy();
|
|
7135
|
+
this.sdkAdapter = null;
|
|
7136
|
+
}
|
|
7137
|
+
}
|
|
7138
|
+
/**
|
|
7139
|
+
* Clean up CLI resources
|
|
7140
|
+
*/
|
|
7141
|
+
async destroyCLI() {
|
|
7142
|
+
if (this.cliWrapper) {
|
|
7143
|
+
await this.cliWrapper.destroy();
|
|
7144
|
+
this.cliWrapper = null;
|
|
7145
|
+
}
|
|
7146
|
+
}
|
|
7147
|
+
/**
|
|
7148
|
+
* Get the configured model
|
|
7149
|
+
*/
|
|
7150
|
+
getModel() {
|
|
7151
|
+
return this.model;
|
|
7152
|
+
}
|
|
7153
|
+
/**
|
|
7154
|
+
* Get the CLI command being used
|
|
7155
|
+
*/
|
|
7156
|
+
getCommand() {
|
|
7157
|
+
return this.cliWrapper?.getCommand() || this.cliConfig.command || "ax-grok";
|
|
7158
|
+
}
|
|
7159
|
+
/**
|
|
7160
|
+
* Get CLI version
|
|
7161
|
+
*/
|
|
7162
|
+
getCLIVersion() {
|
|
7163
|
+
return this.cliWrapper?.getVersion() || null;
|
|
7164
|
+
}
|
|
7165
|
+
};
|
|
7166
|
+
}
|
|
7167
|
+
});
|
|
7168
|
+
|
|
7169
|
+
// src/integrations/ax-grok/sdk-only-adapter.ts
|
|
7170
|
+
var GrokSdkOnlyAdapter;
|
|
7171
|
+
var init_sdk_only_adapter2 = __esm({
|
|
7172
|
+
"src/integrations/ax-grok/sdk-only-adapter.ts"() {
|
|
7173
|
+
init_esm_shims();
|
|
7174
|
+
init_logger();
|
|
7175
|
+
init_sdk_adapter3();
|
|
7176
|
+
init_types3();
|
|
7177
|
+
GrokSdkOnlyAdapter = class {
|
|
7178
|
+
sdkAdapter;
|
|
7179
|
+
initialized = false;
|
|
7180
|
+
model;
|
|
7181
|
+
maxRetries;
|
|
7182
|
+
retryDelayMs;
|
|
7183
|
+
constructor(options = {}) {
|
|
7184
|
+
this.model = options.model || GROK_DEFAULT_MODEL;
|
|
7185
|
+
this.maxRetries = options.maxRetries ?? 2;
|
|
7186
|
+
this.retryDelayMs = options.retryDelayMs ?? 1e3;
|
|
7187
|
+
this.sdkAdapter = new GrokSdkAdapter({
|
|
7188
|
+
model: this.model,
|
|
7189
|
+
apiKey: options.apiKey,
|
|
7190
|
+
baseUrl: options.baseUrl,
|
|
7191
|
+
timeout: options.timeout
|
|
7192
|
+
});
|
|
7193
|
+
logger.debug("[Grok SDK-Only] Adapter created", {
|
|
7194
|
+
model: this.model,
|
|
7195
|
+
maxRetries: this.maxRetries
|
|
7196
|
+
});
|
|
7197
|
+
}
|
|
7198
|
+
/**
|
|
7199
|
+
* Check if SDK is available
|
|
7200
|
+
*/
|
|
7201
|
+
async isAvailable() {
|
|
7202
|
+
return this.sdkAdapter.isAvailable();
|
|
7203
|
+
}
|
|
7204
|
+
/**
|
|
7205
|
+
* Initialize the adapter
|
|
7206
|
+
*/
|
|
7207
|
+
async initialize() {
|
|
7208
|
+
if (this.initialized) {
|
|
7209
|
+
return;
|
|
7210
|
+
}
|
|
7211
|
+
const available = await this.sdkAdapter.isAvailable();
|
|
7212
|
+
if (!available) {
|
|
7213
|
+
throw new Error("Grok SDK not available - check API key and openai package");
|
|
7214
|
+
}
|
|
7215
|
+
await this.sdkAdapter.initialize();
|
|
7216
|
+
this.initialized = true;
|
|
7217
|
+
logger.debug("[Grok SDK-Only] Initialized", { model: this.model });
|
|
7218
|
+
}
|
|
7219
|
+
/**
|
|
7220
|
+
* Execute a request using Grok SDK
|
|
7221
|
+
*
|
|
7222
|
+
* Retries transient errors up to maxRetries times with exponential backoff.
|
|
7223
|
+
* Does NOT fall back to CLI - throws on persistent failure.
|
|
7224
|
+
*/
|
|
7225
|
+
async execute(request) {
|
|
7226
|
+
if (!this.initialized) {
|
|
7227
|
+
await this.initialize();
|
|
7228
|
+
}
|
|
7229
|
+
const startTime = Date.now();
|
|
7230
|
+
let lastError;
|
|
7231
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
7232
|
+
try {
|
|
7233
|
+
const response = await this.sdkAdapter.execute(request);
|
|
7234
|
+
logger.debug("[Grok SDK-Only] Execution succeeded", {
|
|
7235
|
+
model: this.model,
|
|
7236
|
+
attempt: attempt + 1,
|
|
7237
|
+
latencyMs: response.latencyMs
|
|
7238
|
+
});
|
|
7239
|
+
return response;
|
|
7240
|
+
} catch (error) {
|
|
7241
|
+
lastError = error;
|
|
7242
|
+
logger.warn("[Grok SDK-Only] Execution failed", {
|
|
7243
|
+
model: this.model,
|
|
7244
|
+
attempt: attempt + 1,
|
|
7245
|
+
maxRetries: this.maxRetries,
|
|
7246
|
+
error: lastError.message
|
|
7247
|
+
});
|
|
7248
|
+
if (attempt < this.maxRetries && this.isRetryableError(lastError)) {
|
|
7249
|
+
const delay = this.retryDelayMs * Math.pow(2, attempt);
|
|
7250
|
+
logger.debug("[Grok SDK-Only] Retrying after delay", {
|
|
7251
|
+
attempt: attempt + 1,
|
|
7252
|
+
delayMs: delay
|
|
7253
|
+
});
|
|
7254
|
+
await this.sleep(delay);
|
|
7255
|
+
continue;
|
|
7256
|
+
}
|
|
7257
|
+
break;
|
|
7258
|
+
}
|
|
7259
|
+
}
|
|
7260
|
+
const totalTime = Date.now() - startTime;
|
|
7261
|
+
logger.error("[Grok SDK-Only] All retries exhausted", {
|
|
7262
|
+
model: this.model,
|
|
7263
|
+
totalAttempts: this.maxRetries + 1,
|
|
7264
|
+
totalTimeMs: totalTime,
|
|
7265
|
+
lastError: lastError?.message
|
|
7266
|
+
});
|
|
7267
|
+
throw new Error(
|
|
7268
|
+
`Grok SDK execution failed after ${this.maxRetries + 1} attempts: ${lastError?.message || "Unknown error"}`
|
|
7269
|
+
);
|
|
7270
|
+
}
|
|
7271
|
+
/**
|
|
7272
|
+
* Check if error is retryable
|
|
7273
|
+
*/
|
|
7274
|
+
isRetryableError(error) {
|
|
7275
|
+
const message = error.message.toLowerCase();
|
|
7276
|
+
if (message.includes("rate limit") || message.includes("429")) {
|
|
7277
|
+
return true;
|
|
7278
|
+
}
|
|
7279
|
+
if (message.includes("500") || message.includes("502") || message.includes("503") || message.includes("504")) {
|
|
7280
|
+
return true;
|
|
7281
|
+
}
|
|
7282
|
+
if (message.includes("timeout") || message.includes("etimedout")) {
|
|
7283
|
+
return true;
|
|
7284
|
+
}
|
|
7285
|
+
if (message.includes("econnreset") || message.includes("econnrefused")) {
|
|
7286
|
+
return true;
|
|
7287
|
+
}
|
|
7288
|
+
return false;
|
|
7289
|
+
}
|
|
7290
|
+
/**
|
|
7291
|
+
* Sleep utility
|
|
7292
|
+
*/
|
|
7293
|
+
sleep(ms) {
|
|
7294
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
7295
|
+
}
|
|
7296
|
+
/**
|
|
7297
|
+
* Get the configured model
|
|
7298
|
+
*/
|
|
7299
|
+
getModel() {
|
|
7300
|
+
return this.model;
|
|
7301
|
+
}
|
|
7302
|
+
/**
|
|
7303
|
+
* Clean up resources
|
|
7304
|
+
*/
|
|
7305
|
+
async destroy() {
|
|
7306
|
+
await this.sdkAdapter.destroy();
|
|
7307
|
+
this.initialized = false;
|
|
7308
|
+
logger.debug("[Grok SDK-Only] Adapter destroyed");
|
|
7309
|
+
}
|
|
7310
|
+
};
|
|
7311
|
+
}
|
|
7312
|
+
});
|
|
7313
|
+
|
|
7314
|
+
// src/integrations/ax-grok/mcp-client-mode.ts
|
|
7315
|
+
var init_mcp_client_mode2 = __esm({
|
|
7316
|
+
"src/integrations/ax-grok/mcp-client-mode.ts"() {
|
|
7317
|
+
init_esm_shims();
|
|
7318
|
+
init_logger();
|
|
7319
|
+
init_sdk_adapter3();
|
|
7320
|
+
}
|
|
7321
|
+
});
|
|
7322
|
+
|
|
7323
|
+
// src/integrations/ax-grok/index.ts
|
|
7324
|
+
var init_ax_grok = __esm({
|
|
7325
|
+
"src/integrations/ax-grok/index.ts"() {
|
|
7326
|
+
init_esm_shims();
|
|
7327
|
+
init_hybrid_adapter3();
|
|
7328
|
+
init_sdk_adapter3();
|
|
7329
|
+
init_cli_wrapper3();
|
|
7330
|
+
init_sdk_only_adapter2();
|
|
7331
|
+
init_mcp_client_mode2();
|
|
7332
|
+
init_types3();
|
|
7333
|
+
}
|
|
7334
|
+
});
|
|
7335
|
+
|
|
7336
|
+
// src/providers/grok-provider.ts
|
|
7337
|
+
var grok_provider_exports = {};
|
|
7338
|
+
__export(grok_provider_exports, {
|
|
7339
|
+
GrokProvider: () => GrokProvider,
|
|
7340
|
+
default: () => GrokProvider
|
|
7341
|
+
});
|
|
7342
|
+
var MODEL_MAPPING2, GrokProvider;
|
|
7343
|
+
var init_grok_provider = __esm({
|
|
7344
|
+
"src/providers/grok-provider.ts"() {
|
|
7345
|
+
init_esm_shims();
|
|
7346
|
+
init_base_provider();
|
|
7347
|
+
init_logger();
|
|
7348
|
+
init_ax_grok();
|
|
7349
|
+
MODEL_MAPPING2 = {
|
|
7350
|
+
"grok-beta": "grok-3"
|
|
7351
|
+
};
|
|
7352
|
+
GrokProvider = class _GrokProvider extends BaseProvider {
|
|
7353
|
+
/** Selected model */
|
|
7354
|
+
model;
|
|
7355
|
+
/** SDK-only adapter for direct execution (v13.0.0) */
|
|
7356
|
+
sdkOnlyAdapter = null;
|
|
7357
|
+
/** Legacy hybrid adapter for 'auto' mode (backwards compatibility) */
|
|
7358
|
+
hybridAdapter = null;
|
|
7359
|
+
/** Provider configuration */
|
|
7360
|
+
grokConfig;
|
|
7361
|
+
/** Supported models */
|
|
7362
|
+
static SUPPORTED_MODELS = [
|
|
7363
|
+
"grok-3",
|
|
7364
|
+
"grok-3-mini",
|
|
7365
|
+
"grok-2-vision",
|
|
7366
|
+
"grok-2",
|
|
7367
|
+
// Legacy alias
|
|
7368
|
+
"grok-beta"
|
|
7369
|
+
];
|
|
7370
|
+
constructor(config) {
|
|
7371
|
+
super({
|
|
7372
|
+
...config,
|
|
7373
|
+
command: "ax-grok"
|
|
7374
|
+
});
|
|
7375
|
+
this.grokConfig = config;
|
|
7376
|
+
const requestedModel = config.model || "grok-3";
|
|
7377
|
+
if (!_GrokProvider.SUPPORTED_MODELS.includes(requestedModel)) {
|
|
7378
|
+
logger.warn(`[Grok] Unknown model: ${requestedModel}. Using grok-3.`);
|
|
7379
|
+
this.model = "grok-3";
|
|
7380
|
+
} else {
|
|
7381
|
+
this.model = requestedModel;
|
|
7382
|
+
}
|
|
7383
|
+
logger.debug("[Grok Provider] Initialized", {
|
|
7384
|
+
model: this.model,
|
|
7385
|
+
mode: config.mode || "auto"
|
|
7386
|
+
});
|
|
7387
|
+
}
|
|
7388
|
+
/**
|
|
7389
|
+
* Get the normalized model name
|
|
7390
|
+
*/
|
|
7391
|
+
getNormalizedModel() {
|
|
7392
|
+
return MODEL_MAPPING2[this.model] || this.model;
|
|
7393
|
+
}
|
|
7394
|
+
/**
|
|
7395
|
+
* Get or create SDK-only adapter (v13.0.0 default)
|
|
7396
|
+
*/
|
|
7397
|
+
getSdkOnlyAdapter() {
|
|
7398
|
+
if (!this.sdkOnlyAdapter) {
|
|
7399
|
+
this.sdkOnlyAdapter = new GrokSdkOnlyAdapter({
|
|
7400
|
+
model: this.model,
|
|
7401
|
+
apiKey: this.grokConfig.apiKey,
|
|
7402
|
+
baseUrl: this.grokConfig.baseUrl,
|
|
7403
|
+
timeout: this.grokConfig.timeout
|
|
7404
|
+
});
|
|
7405
|
+
}
|
|
7406
|
+
return this.sdkOnlyAdapter;
|
|
7407
|
+
}
|
|
7408
|
+
/**
|
|
7409
|
+
* Get or create hybrid adapter (legacy, for 'auto' mode only)
|
|
7410
|
+
*/
|
|
7411
|
+
getHybridAdapter() {
|
|
7412
|
+
if (!this.hybridAdapter) {
|
|
7413
|
+
const options = {
|
|
7414
|
+
mode: this.grokConfig.mode || "auto",
|
|
7415
|
+
model: this.model,
|
|
7416
|
+
apiKey: this.grokConfig.apiKey,
|
|
7417
|
+
baseUrl: this.grokConfig.baseUrl,
|
|
7418
|
+
command: "ax-grok",
|
|
7419
|
+
timeout: this.grokConfig.timeout
|
|
7420
|
+
};
|
|
7421
|
+
this.hybridAdapter = new GrokHybridAdapter(options);
|
|
7422
|
+
}
|
|
7423
|
+
return this.hybridAdapter;
|
|
7424
|
+
}
|
|
7425
|
+
/**
|
|
7426
|
+
* Execute a task using Grok
|
|
7427
|
+
*
|
|
7428
|
+
* Execution flow (v13.0.0):
|
|
7429
|
+
* 1. Mock mode → return mock response
|
|
7430
|
+
* 2. mode='sdk' (default) → use SDK-only adapter (NO CLI fallback)
|
|
7431
|
+
* 3. mode='auto' (legacy) → use hybrid adapter (SDK with CLI fallback)
|
|
7432
|
+
* 4. mode='cli' → use CLI via BaseProvider (deprecated for Grok)
|
|
7433
|
+
*/
|
|
7434
|
+
async execute(request) {
|
|
7435
|
+
if (process.env.AX_MOCK_PROVIDERS === "true") {
|
|
7436
|
+
return this.createMockResponse(request.prompt);
|
|
7437
|
+
}
|
|
7438
|
+
const effectiveMode = this.grokConfig.mode || "sdk";
|
|
7439
|
+
if (effectiveMode === "cli") {
|
|
7440
|
+
logger.warn("[Grok Provider] CLI mode is deprecated for Grok. Consider using SDK mode.", {
|
|
7441
|
+
model: this.model
|
|
7442
|
+
});
|
|
7443
|
+
return super.execute(request);
|
|
7444
|
+
}
|
|
7445
|
+
if (effectiveMode === "auto") {
|
|
7446
|
+
logger.debug("[Grok Provider] Executing via hybrid adapter (legacy auto mode)", {
|
|
7447
|
+
promptLength: request.prompt.length,
|
|
7448
|
+
model: this.model
|
|
7449
|
+
});
|
|
7450
|
+
const adapter2 = this.getHybridAdapter();
|
|
7451
|
+
return adapter2.execute(request);
|
|
7452
|
+
}
|
|
7453
|
+
logger.debug("[Grok Provider] Executing via SDK-only adapter", {
|
|
7454
|
+
promptLength: request.prompt.length,
|
|
7455
|
+
model: this.model
|
|
7456
|
+
});
|
|
7457
|
+
const adapter = this.getSdkOnlyAdapter();
|
|
7458
|
+
return adapter.execute(request);
|
|
7459
|
+
}
|
|
7460
|
+
/**
|
|
7461
|
+
* Get CLI command
|
|
7462
|
+
*/
|
|
7463
|
+
getCLICommand() {
|
|
7464
|
+
const adapter = this.hybridAdapter;
|
|
7465
|
+
if (adapter) {
|
|
7466
|
+
const activeMode = adapter.getActiveMode();
|
|
7467
|
+
if (activeMode === "sdk") {
|
|
7468
|
+
return "grok-sdk";
|
|
7469
|
+
}
|
|
7470
|
+
}
|
|
7471
|
+
return "ax-grok";
|
|
7472
|
+
}
|
|
7473
|
+
/**
|
|
7474
|
+
* Get CLI arguments for ax-grok headless mode
|
|
7475
|
+
*/
|
|
7476
|
+
getCLIArgs() {
|
|
7477
|
+
const args2 = ["-p"];
|
|
7478
|
+
const normalizedModel = this.getNormalizedModel();
|
|
7479
|
+
if (normalizedModel !== "grok-3") {
|
|
7480
|
+
args2.push("--model", normalizedModel);
|
|
7481
|
+
}
|
|
7482
|
+
return args2;
|
|
7483
|
+
}
|
|
7484
|
+
/**
|
|
7485
|
+
* Create mock response for testing
|
|
7486
|
+
*/
|
|
7487
|
+
createMockResponse(prompt) {
|
|
7488
|
+
return {
|
|
7489
|
+
content: this.getMockResponse(),
|
|
7490
|
+
model: this.getNormalizedModel(),
|
|
7491
|
+
tokensUsed: {
|
|
7492
|
+
prompt: this.estimateTokens(prompt),
|
|
7493
|
+
completion: 50,
|
|
7494
|
+
total: this.estimateTokens(prompt) + 50
|
|
7495
|
+
},
|
|
7496
|
+
latencyMs: 10,
|
|
7497
|
+
finishReason: "stop",
|
|
7498
|
+
cached: false
|
|
7499
|
+
};
|
|
7500
|
+
}
|
|
7501
|
+
/**
|
|
7502
|
+
* Estimate token count
|
|
7503
|
+
*/
|
|
7504
|
+
estimateTokens(text) {
|
|
7505
|
+
return Math.ceil(text.length / 4);
|
|
7506
|
+
}
|
|
7507
|
+
/**
|
|
7508
|
+
* Get mock response for testing
|
|
7509
|
+
*/
|
|
7510
|
+
getMockResponse() {
|
|
7511
|
+
return `[Mock Grok Response]
|
|
7512
|
+
|
|
7513
|
+
This is a mock response from the Grok provider (${this.getNormalizedModel()}).
|
|
7514
|
+
In production, this would be a response from ${this.grokConfig.mode === "sdk" ? "Grok SDK" : "ax-grok CLI"}.
|
|
7515
|
+
|
|
7516
|
+
Model: ${this.getNormalizedModel()}
|
|
7517
|
+
Provider: Grok (xAI)
|
|
7518
|
+
Mode: ${this.grokConfig.mode || "auto"}`;
|
|
7519
|
+
}
|
|
7520
|
+
/**
|
|
7521
|
+
* Get provider capabilities
|
|
7522
|
+
*/
|
|
7523
|
+
get capabilities() {
|
|
7524
|
+
const model = this.getNormalizedModel();
|
|
7525
|
+
const isVision = model.includes("vision") || model === "grok-2-vision";
|
|
7526
|
+
const maxContextTokens = 131072;
|
|
7527
|
+
const activeMode = this.hybridAdapter?.getActiveMode();
|
|
7528
|
+
const integrationMode = activeMode === "sdk" ? "sdk" : "cli";
|
|
7529
|
+
return {
|
|
7530
|
+
...super.capabilities,
|
|
7531
|
+
supportsStreaming: true,
|
|
7532
|
+
supportsVision: isVision,
|
|
7533
|
+
maxContextTokens,
|
|
7534
|
+
supportedModels: _GrokProvider.SUPPORTED_MODELS,
|
|
7535
|
+
integrationMode
|
|
7536
|
+
};
|
|
7537
|
+
}
|
|
7538
|
+
/**
|
|
7539
|
+
* Get the active execution mode
|
|
7540
|
+
*/
|
|
7541
|
+
getActiveMode() {
|
|
7542
|
+
return this.hybridAdapter?.getActiveMode() || null;
|
|
7543
|
+
}
|
|
7544
|
+
/**
|
|
7545
|
+
* Reset circuit breakers
|
|
7546
|
+
*/
|
|
7547
|
+
resetCircuitBreakers() {
|
|
7548
|
+
this.hybridAdapter?.resetCircuitBreakers();
|
|
7549
|
+
}
|
|
7550
|
+
/**
|
|
7551
|
+
* Clean up resources
|
|
7552
|
+
*/
|
|
7553
|
+
async destroy() {
|
|
7554
|
+
if (this.sdkOnlyAdapter) {
|
|
7555
|
+
await this.sdkOnlyAdapter.destroy();
|
|
7556
|
+
this.sdkOnlyAdapter = null;
|
|
7557
|
+
}
|
|
7558
|
+
if (this.hybridAdapter) {
|
|
7559
|
+
await this.hybridAdapter.destroy();
|
|
7560
|
+
this.hybridAdapter = null;
|
|
7561
|
+
}
|
|
7562
|
+
}
|
|
7563
|
+
/**
|
|
7564
|
+
* Get the list of supported models
|
|
7565
|
+
*/
|
|
7566
|
+
static getSupportedModels() {
|
|
7567
|
+
return [..._GrokProvider.SUPPORTED_MODELS];
|
|
7568
|
+
}
|
|
7569
|
+
};
|
|
4144
7570
|
}
|
|
4145
7571
|
});
|
|
4146
7572
|
|
|
@@ -4149,18 +7575,19 @@ init_esm_shims();
|
|
|
4149
7575
|
|
|
4150
7576
|
// src/mcp/server.ts
|
|
4151
7577
|
init_esm_shims();
|
|
7578
|
+
init_validation_limits();
|
|
4152
7579
|
|
|
4153
7580
|
// src/shared/helpers/version.ts
|
|
4154
7581
|
init_esm_shims();
|
|
4155
|
-
var
|
|
7582
|
+
var __dirname2 = dirname(fileURLToPath(import.meta.url));
|
|
4156
7583
|
function getVersion() {
|
|
4157
7584
|
try {
|
|
4158
7585
|
const possiblePaths = [
|
|
4159
|
-
join(
|
|
7586
|
+
join(__dirname2, "../../../package.json"),
|
|
4160
7587
|
// From src/shared/helpers
|
|
4161
|
-
join(
|
|
7588
|
+
join(__dirname2, "../../package.json"),
|
|
4162
7589
|
// From dist/mcp (bundled MCP entry)
|
|
4163
|
-
join(
|
|
7590
|
+
join(__dirname2, "../package.json")
|
|
4164
7591
|
// From dist
|
|
4165
7592
|
];
|
|
4166
7593
|
for (const pkgPath of possiblePaths) {
|
|
@@ -4182,7 +7609,8 @@ function getVersion() {
|
|
|
4182
7609
|
|
|
4183
7610
|
// src/mcp/types.ts
|
|
4184
7611
|
init_esm_shims();
|
|
4185
|
-
var MCP_PROTOCOL_VERSION = "
|
|
7612
|
+
var MCP_PROTOCOL_VERSION = "2025-11-25";
|
|
7613
|
+
var MCP_SUPPORTED_VERSIONS = ["2025-11-25", "2024-12-05", "2024-11-05"];
|
|
4186
7614
|
|
|
4187
7615
|
// src/mcp/server.ts
|
|
4188
7616
|
init_logger();
|
|
@@ -4618,119 +8046,8 @@ function deepMerge(defaults, user) {
|
|
|
4618
8046
|
return result;
|
|
4619
8047
|
}
|
|
4620
8048
|
|
|
4621
|
-
// src/core/
|
|
4622
|
-
|
|
4623
|
-
var VALIDATION_LIMITS = {
|
|
4624
|
-
// Resource limits (prevent DoS)
|
|
4625
|
-
MAX_ENTRIES: 1e6,
|
|
4626
|
-
// 1 million entries (memory, cache)
|
|
4627
|
-
MAX_TIMEOUT: 36e5,
|
|
4628
|
-
// 1 hour (execution, delegation)
|
|
4629
|
-
MAX_FILE_SIZE: 104857600,
|
|
4630
|
-
// 100 MB (workspace, abilities)
|
|
4631
|
-
MAX_CACHE_SIZE: 524288e3,
|
|
4632
|
-
// 500 MB (cache storage)
|
|
4633
|
-
MAX_SESSIONS: 1e4,
|
|
4634
|
-
// 10k concurrent sessions
|
|
4635
|
-
// Performance limits (prevent resource exhaustion)
|
|
4636
|
-
MAX_CONCURRENT_AGENTS: 100,
|
|
4637
|
-
// Maximum concurrent agents
|
|
4638
|
-
MAX_CPU_PERCENT: 80,
|
|
4639
|
-
// Maximum CPU usage percent
|
|
4640
|
-
MAX_MEMORY_MB: 2048,
|
|
4641
|
-
// Maximum memory usage in MB
|
|
4642
|
-
// Config validation limits
|
|
4643
|
-
MAX_CONFIG_FILE_SIZE: 1048576,
|
|
4644
|
-
// 1MB max config file size
|
|
4645
|
-
MAX_NAME_LENGTH: 50,
|
|
4646
|
-
// Max name length for agents/providers
|
|
4647
|
-
MAX_COMMAND_LENGTH: 100,
|
|
4648
|
-
// Max command length
|
|
4649
|
-
MAX_ARRAY_LENGTH: 1e4,
|
|
4650
|
-
// Max array elements in config
|
|
4651
|
-
// Path and file limits
|
|
4652
|
-
MIN_FILE_SIZE: 1,
|
|
4653
|
-
// Minimum file size in bytes
|
|
4654
|
-
MIN_TIMEOUT: 1e3,
|
|
4655
|
-
// Minimum timeout in ms (1 second)
|
|
4656
|
-
MIN_INTERVAL: 100,
|
|
4657
|
-
// Minimum interval in ms
|
|
4658
|
-
MIN_BACKOFF_FACTOR: 1,
|
|
4659
|
-
// Minimum backoff multiplier
|
|
4660
|
-
MAX_BACKOFF_FACTOR: 5,
|
|
4661
|
-
// Maximum backoff multiplier
|
|
4662
|
-
MAX_TTL: 864e5,
|
|
4663
|
-
// Maximum TTL in ms (24 hours)
|
|
4664
|
-
MIN_BYTES: 1,
|
|
4665
|
-
// Minimum bytes for file sizes
|
|
4666
|
-
// Network limits
|
|
4667
|
-
MIN_PORT: 1024,
|
|
4668
|
-
// Minimum port number
|
|
4669
|
-
MAX_PORT: 65535
|
|
4670
|
-
// Maximum port number
|
|
4671
|
-
};
|
|
4672
|
-
function isValidRelativePath(path7) {
|
|
4673
|
-
if (!path7 || typeof path7 !== "string") {
|
|
4674
|
-
return false;
|
|
4675
|
-
}
|
|
4676
|
-
const normalizedPath = path7.replace(/\\/g, "/");
|
|
4677
|
-
if (normalizedPath.startsWith("/")) {
|
|
4678
|
-
return false;
|
|
4679
|
-
}
|
|
4680
|
-
if (normalizedPath.includes("..")) {
|
|
4681
|
-
return false;
|
|
4682
|
-
}
|
|
4683
|
-
if (/^[a-zA-Z]:/.test(normalizedPath)) {
|
|
4684
|
-
return false;
|
|
4685
|
-
}
|
|
4686
|
-
if (normalizedPath.startsWith("//")) {
|
|
4687
|
-
return false;
|
|
4688
|
-
}
|
|
4689
|
-
return true;
|
|
4690
|
-
}
|
|
4691
|
-
function isValidCommand(command) {
|
|
4692
|
-
if (!command || typeof command !== "string") {
|
|
4693
|
-
return false;
|
|
4694
|
-
}
|
|
4695
|
-
if (command.length > VALIDATION_LIMITS.MAX_COMMAND_LENGTH) {
|
|
4696
|
-
return false;
|
|
4697
|
-
}
|
|
4698
|
-
if (!/^[a-z0-9_-]+$/i.test(command)) {
|
|
4699
|
-
return false;
|
|
4700
|
-
}
|
|
4701
|
-
return true;
|
|
4702
|
-
}
|
|
4703
|
-
function isValidName(name) {
|
|
4704
|
-
if (!name || typeof name !== "string") {
|
|
4705
|
-
return false;
|
|
4706
|
-
}
|
|
4707
|
-
if (name.length > VALIDATION_LIMITS.MAX_NAME_LENGTH) {
|
|
4708
|
-
return false;
|
|
4709
|
-
}
|
|
4710
|
-
if (!/^[a-z0-9][a-z0-9-_]*$/i.test(name)) {
|
|
4711
|
-
return false;
|
|
4712
|
-
}
|
|
4713
|
-
return true;
|
|
4714
|
-
}
|
|
4715
|
-
function isValidExtension(ext) {
|
|
4716
|
-
if (!ext || typeof ext !== "string") {
|
|
4717
|
-
return false;
|
|
4718
|
-
}
|
|
4719
|
-
const normalized = ext.startsWith(".") ? ext : `.${ext}`;
|
|
4720
|
-
if (normalized.length > 10 || normalized.length < 2) {
|
|
4721
|
-
return false;
|
|
4722
|
-
}
|
|
4723
|
-
if (!/^\.[a-z0-9]+$/i.test(normalized)) {
|
|
4724
|
-
return false;
|
|
4725
|
-
}
|
|
4726
|
-
return true;
|
|
4727
|
-
}
|
|
4728
|
-
function isPositiveInteger(value) {
|
|
4729
|
-
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
4730
|
-
}
|
|
4731
|
-
function isNonNegativeInteger(value) {
|
|
4732
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
4733
|
-
}
|
|
8049
|
+
// src/core/config/loader.ts
|
|
8050
|
+
init_validation_limits();
|
|
4734
8051
|
|
|
4735
8052
|
// src/core/cache/cache.ts
|
|
4736
8053
|
init_esm_shims();
|
|
@@ -4806,7 +8123,7 @@ var TTLCache = class {
|
|
|
4806
8123
|
* @param value Value to cache
|
|
4807
8124
|
* @param customTTL Optional custom TTL for this entry (ms)
|
|
4808
8125
|
*/
|
|
4809
|
-
set(key, value,
|
|
8126
|
+
set(key, value, _customTTL) {
|
|
4810
8127
|
const size = this.estimateSize(value);
|
|
4811
8128
|
if (this.config.maxSize > 0 && size > this.config.maxSize) {
|
|
4812
8129
|
logger.warn("Entry too large for cache", {
|
|
@@ -5063,11 +8380,12 @@ function calculateMaxConcurrentAgents(staticLimit) {
|
|
|
5063
8380
|
init_esm_shims();
|
|
5064
8381
|
var PRECOMPILED_CONFIG = {
|
|
5065
8382
|
"providers": {
|
|
5066
|
-
"
|
|
8383
|
+
"gemini-cli": {
|
|
5067
8384
|
"enabled": true,
|
|
5068
|
-
"priority":
|
|
8385
|
+
"priority": 1,
|
|
5069
8386
|
"timeout": 27e5,
|
|
5070
|
-
"command": "
|
|
8387
|
+
"command": "gemini",
|
|
8388
|
+
"description": "#1 Frontend/Design, multimodal, free tier, WebDev Arena leader",
|
|
5071
8389
|
"healthCheck": {
|
|
5072
8390
|
"enabled": true,
|
|
5073
8391
|
"interval": 3e5,
|
|
@@ -5089,15 +8407,16 @@ var PRECOMPILED_CONFIG = {
|
|
|
5089
8407
|
},
|
|
5090
8408
|
"limitTracking": {
|
|
5091
8409
|
"enabled": true,
|
|
5092
|
-
"window": "
|
|
8410
|
+
"window": "daily",
|
|
5093
8411
|
"resetHourUtc": 0
|
|
5094
8412
|
}
|
|
5095
8413
|
},
|
|
5096
|
-
"
|
|
8414
|
+
"openai": {
|
|
5097
8415
|
"enabled": true,
|
|
5098
|
-
"priority":
|
|
8416
|
+
"priority": 3,
|
|
5099
8417
|
"timeout": 27e5,
|
|
5100
|
-
"command": "
|
|
8418
|
+
"command": "codex",
|
|
8419
|
+
"description": "Best reasoning (o3), strategy, 75% accuracy, 192k context",
|
|
5101
8420
|
"healthCheck": {
|
|
5102
8421
|
"enabled": true,
|
|
5103
8422
|
"interval": 3e5,
|
|
@@ -5123,11 +8442,12 @@ var PRECOMPILED_CONFIG = {
|
|
|
5123
8442
|
"resetHourUtc": 0
|
|
5124
8443
|
}
|
|
5125
8444
|
},
|
|
5126
|
-
"
|
|
8445
|
+
"claude-code": {
|
|
5127
8446
|
"enabled": true,
|
|
5128
|
-
"priority":
|
|
8447
|
+
"priority": 2,
|
|
5129
8448
|
"timeout": 27e5,
|
|
5130
|
-
"command": "
|
|
8449
|
+
"command": "claude",
|
|
8450
|
+
"description": "#1 Coding model, agentic workflows, security, 0% edit error rate",
|
|
5131
8451
|
"healthCheck": {
|
|
5132
8452
|
"enabled": true,
|
|
5133
8453
|
"interval": 3e5,
|
|
@@ -5147,6 +8467,50 @@ var PRECOMPILED_CONFIG = {
|
|
|
5147
8467
|
"forceKillDelay": 1e3,
|
|
5148
8468
|
"cacheEnabled": true
|
|
5149
8469
|
},
|
|
8470
|
+
"limitTracking": {
|
|
8471
|
+
"enabled": true,
|
|
8472
|
+
"window": "weekly",
|
|
8473
|
+
"resetHourUtc": 0
|
|
8474
|
+
}
|
|
8475
|
+
},
|
|
8476
|
+
"glm": {
|
|
8477
|
+
"enabled": true,
|
|
8478
|
+
"priority": 4,
|
|
8479
|
+
"timeout": 27e5,
|
|
8480
|
+
"type": "sdk",
|
|
8481
|
+
"description": "Near Claude coding (48.6%), low cost $3/mo, 200k context, agentic",
|
|
8482
|
+
"healthCheck": {
|
|
8483
|
+
"enabled": true,
|
|
8484
|
+
"interval": 3e5,
|
|
8485
|
+
"timeout": 5e3
|
|
8486
|
+
},
|
|
8487
|
+
"circuitBreaker": {
|
|
8488
|
+
"enabled": true,
|
|
8489
|
+
"failureThreshold": 3,
|
|
8490
|
+
"recoveryTimeout": 6e4
|
|
8491
|
+
},
|
|
8492
|
+
"limitTracking": {
|
|
8493
|
+
"enabled": true,
|
|
8494
|
+
"window": "daily",
|
|
8495
|
+
"resetHourUtc": 0
|
|
8496
|
+
}
|
|
8497
|
+
},
|
|
8498
|
+
"grok": {
|
|
8499
|
+
"enabled": true,
|
|
8500
|
+
"priority": 5,
|
|
8501
|
+
"timeout": 27e5,
|
|
8502
|
+
"type": "sdk",
|
|
8503
|
+
"description": "Fastest (67ms), reasoning (93% AIME), 1M context, DeepSearch",
|
|
8504
|
+
"healthCheck": {
|
|
8505
|
+
"enabled": true,
|
|
8506
|
+
"interval": 3e5,
|
|
8507
|
+
"timeout": 5e3
|
|
8508
|
+
},
|
|
8509
|
+
"circuitBreaker": {
|
|
8510
|
+
"enabled": true,
|
|
8511
|
+
"failureThreshold": 3,
|
|
8512
|
+
"recoveryTimeout": 6e4
|
|
8513
|
+
},
|
|
5150
8514
|
"limitTracking": {
|
|
5151
8515
|
"enabled": true,
|
|
5152
8516
|
"window": "daily",
|
|
@@ -5363,11 +8727,12 @@ var PRECOMPILED_CONFIG = {
|
|
|
5363
8727
|
"enableFreeTierPrioritization": true,
|
|
5364
8728
|
"enableWorkloadAwareRouting": true
|
|
5365
8729
|
},
|
|
5366
|
-
"version": "12.
|
|
8730
|
+
"version": "12.4.1"
|
|
5367
8731
|
};
|
|
5368
8732
|
|
|
5369
8733
|
// src/core/config/schemas.ts
|
|
5370
8734
|
init_esm_shims();
|
|
8735
|
+
init_validation_limits();
|
|
5371
8736
|
z.enum([
|
|
5372
8737
|
"claude",
|
|
5373
8738
|
"claude-code",
|
|
@@ -5430,17 +8795,35 @@ var limitTrackingConfigSchema = z.object({
|
|
|
5430
8795
|
resetHourUtc: z.number().int().min(0).max(23).default(0),
|
|
5431
8796
|
customResetMs: positiveIntSchema.optional()
|
|
5432
8797
|
}).describe("Provider limit tracking configuration");
|
|
8798
|
+
var providerTypeSchema = z.enum(["cli", "sdk", "hybrid"]).default("cli");
|
|
5433
8799
|
var providerConfigSchema = z.object({
|
|
5434
8800
|
enabled: z.boolean().default(true),
|
|
5435
8801
|
priority: positiveIntSchema,
|
|
5436
8802
|
timeout: timeoutSchema,
|
|
5437
|
-
|
|
8803
|
+
type: providerTypeSchema.optional(),
|
|
8804
|
+
command: commandSchema.optional(),
|
|
8805
|
+
// Optional for SDK providers (glm, grok)
|
|
8806
|
+
description: z.string().optional(),
|
|
8807
|
+
// Provider description
|
|
8808
|
+
model: z.string().optional(),
|
|
8809
|
+
// Model name override
|
|
5438
8810
|
healthCheck: healthCheckConfigSchema.optional(),
|
|
5439
8811
|
circuitBreaker: circuitBreakerConfigSchema.optional(),
|
|
5440
8812
|
processManagement: processManagementConfigSchema.optional(),
|
|
5441
8813
|
versionDetection: versionDetectionConfigSchema.optional(),
|
|
5442
8814
|
limitTracking: limitTrackingConfigSchema.optional()
|
|
5443
|
-
}).
|
|
8815
|
+
}).refine(
|
|
8816
|
+
(data) => {
|
|
8817
|
+
if (data.type === "sdk") {
|
|
8818
|
+
return true;
|
|
8819
|
+
}
|
|
8820
|
+
return data.command !== void 0;
|
|
8821
|
+
},
|
|
8822
|
+
{
|
|
8823
|
+
message: "CLI and hybrid providers require a command field",
|
|
8824
|
+
path: ["command"]
|
|
8825
|
+
}
|
|
8826
|
+
).describe("Provider configuration");
|
|
5444
8827
|
var providersConfigSchema = z.record(
|
|
5445
8828
|
safeNameSchema,
|
|
5446
8829
|
providerConfigSchema
|
|
@@ -5826,9 +9209,15 @@ function validateConfig(config) {
|
|
|
5826
9209
|
if (!isValidName(name)) {
|
|
5827
9210
|
errors.push(`Provider "${name}": name invalid (use alphanumeric, dash, underscore only, max 50 chars)`);
|
|
5828
9211
|
}
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
9212
|
+
const providerType = provider.type;
|
|
9213
|
+
const isSDKProvider = providerType === "sdk";
|
|
9214
|
+
if (!isSDKProvider) {
|
|
9215
|
+
if (!provider.command) {
|
|
9216
|
+
errors.push(`Provider ${name}: command is required for CLI/hybrid providers`);
|
|
9217
|
+
} else if (!isValidCommand(provider.command)) {
|
|
9218
|
+
errors.push(`Provider ${name}: command invalid (alphanumeric, dash, underscore only, max 100 chars, no shell metacharacters)`);
|
|
9219
|
+
}
|
|
9220
|
+
} else if (provider.command && !isValidCommand(provider.command)) {
|
|
5832
9221
|
errors.push(`Provider ${name}: command invalid (alphanumeric, dash, underscore only, max 100 chars, no shell metacharacters)`);
|
|
5833
9222
|
}
|
|
5834
9223
|
if (!isPositiveInteger(provider.priority)) {
|
|
@@ -6204,6 +9593,7 @@ function validateConfig(config) {
|
|
|
6204
9593
|
init_esm_shims();
|
|
6205
9594
|
init_logger();
|
|
6206
9595
|
init_errors();
|
|
9596
|
+
init_validation_limits();
|
|
6207
9597
|
|
|
6208
9598
|
// src/core/provider-limit-manager.ts
|
|
6209
9599
|
init_esm_shims();
|
|
@@ -6414,6 +9804,9 @@ var PathResolver = class {
|
|
|
6414
9804
|
}
|
|
6415
9805
|
}
|
|
6416
9806
|
};
|
|
9807
|
+
|
|
9808
|
+
// src/core/provider-limit-manager.ts
|
|
9809
|
+
init_validation_limits();
|
|
6417
9810
|
var ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
|
|
6418
9811
|
static instance = null;
|
|
6419
9812
|
stateFilePath;
|
|
@@ -6439,7 +9832,7 @@ var ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
|
|
|
6439
9832
|
/**
|
|
6440
9833
|
* Get singleton instance
|
|
6441
9834
|
*/
|
|
6442
|
-
static getInstance(stateDirectory =
|
|
9835
|
+
static getInstance(stateDirectory = AX_PATHS.STATE) {
|
|
6443
9836
|
if (!_ProviderLimitManager.instance) {
|
|
6444
9837
|
_ProviderLimitManager.instance = new _ProviderLimitManager(stateDirectory);
|
|
6445
9838
|
}
|
|
@@ -6527,7 +9920,9 @@ var ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
|
|
|
6527
9920
|
return { isLimited: false };
|
|
6528
9921
|
}
|
|
6529
9922
|
if (now >= state.resetAtMs) {
|
|
6530
|
-
void this.clearLimit(providerName, "expired")
|
|
9923
|
+
void this.clearLimit(providerName, "expired").catch((err) => {
|
|
9924
|
+
logger.warn("Failed to clear expired limit", { provider: providerName, error: err.message });
|
|
9925
|
+
});
|
|
6531
9926
|
return { isLimited: false };
|
|
6532
9927
|
}
|
|
6533
9928
|
const remainingMs = state.resetAtMs - now;
|
|
@@ -6616,9 +10011,10 @@ var ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
|
|
|
6616
10011
|
getManualOverride() {
|
|
6617
10012
|
if (this.manualOverride && this.manualOverride.expiresAtMs) {
|
|
6618
10013
|
if (Date.now() >= this.manualOverride.expiresAtMs) {
|
|
6619
|
-
this.manualOverride;
|
|
6620
10014
|
this.manualOverride = void 0;
|
|
6621
|
-
void this.clearManualOverride()
|
|
10015
|
+
void this.clearManualOverride().catch((err) => {
|
|
10016
|
+
logger.warn("Failed to clear expired manual override", { error: err.message });
|
|
10017
|
+
});
|
|
6622
10018
|
return void 0;
|
|
6623
10019
|
}
|
|
6624
10020
|
}
|
|
@@ -6755,10 +10151,10 @@ var ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
|
|
|
6755
10151
|
async function getProviderLimitManager(stateDirectory) {
|
|
6756
10152
|
if (!stateDirectory) {
|
|
6757
10153
|
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
6758
|
-
stateDirectory =
|
|
10154
|
+
stateDirectory = AX_PATHS.STATE;
|
|
6759
10155
|
} else {
|
|
6760
10156
|
const projectRoot = await detectProjectRoot();
|
|
6761
|
-
stateDirectory = path4.join(projectRoot,
|
|
10157
|
+
stateDirectory = path4.join(projectRoot, AX_PATHS.STATE);
|
|
6762
10158
|
}
|
|
6763
10159
|
}
|
|
6764
10160
|
return ProviderLimitManager.getInstance(stateDirectory);
|
|
@@ -7741,13 +11137,12 @@ function getRoutingStrategyManager(config) {
|
|
|
7741
11137
|
// src/core/router/circuit-breaker.ts
|
|
7742
11138
|
init_esm_shims();
|
|
7743
11139
|
init_logger();
|
|
11140
|
+
init_validation_limits();
|
|
7744
11141
|
var DEFAULT_CONFIG2 = {
|
|
7745
11142
|
failureThreshold: 3,
|
|
7746
|
-
cooldownMs:
|
|
7747
|
-
// 1 minute
|
|
11143
|
+
cooldownMs: TIMEOUTS.CIRCUIT_BREAKER_RECOVERY,
|
|
7748
11144
|
successThreshold: 2,
|
|
7749
|
-
failureWindowMs:
|
|
7750
|
-
// v11.1.0: 5 minutes - failures outside this window don't count
|
|
11145
|
+
failureWindowMs: TIMEOUTS.CIRCUIT_BREAKER_WINDOW
|
|
7751
11146
|
};
|
|
7752
11147
|
var CircuitBreaker = class {
|
|
7753
11148
|
config;
|
|
@@ -7904,6 +11299,7 @@ var CircuitBreaker = class {
|
|
|
7904
11299
|
// src/core/router/trace-logger.ts
|
|
7905
11300
|
init_esm_shims();
|
|
7906
11301
|
init_logger();
|
|
11302
|
+
init_validation_limits();
|
|
7907
11303
|
var RouterTraceLogger = class {
|
|
7908
11304
|
traceFile;
|
|
7909
11305
|
stream;
|
|
@@ -7915,7 +11311,7 @@ var RouterTraceLogger = class {
|
|
|
7915
11311
|
sigintHandler = () => this.close();
|
|
7916
11312
|
sigtermHandler = () => this.close();
|
|
7917
11313
|
constructor(options) {
|
|
7918
|
-
this.traceFile = join(options.workspacePath, "
|
|
11314
|
+
this.traceFile = join(options.workspacePath, AX_PATHS.LOGS, "router.trace.jsonl");
|
|
7919
11315
|
this.enabled = options.enabled ?? true;
|
|
7920
11316
|
this.autoFlush = options.autoFlush ?? true;
|
|
7921
11317
|
if (this.enabled) {
|
|
@@ -8302,13 +11698,12 @@ var Router = class {
|
|
|
8302
11698
|
return a.priority - b.priority;
|
|
8303
11699
|
});
|
|
8304
11700
|
this.fallbackEnabled = config.fallbackEnabled;
|
|
8305
|
-
this.providerCooldownMs = config.providerCooldownMs ??
|
|
11701
|
+
this.providerCooldownMs = config.providerCooldownMs ?? TIMEOUTS.PROVIDER_COOLDOWN;
|
|
8306
11702
|
this.cache = config.cache;
|
|
8307
11703
|
this.circuitBreaker = new CircuitBreaker({
|
|
8308
11704
|
failureThreshold: config.circuitBreakerThreshold ?? 3,
|
|
8309
|
-
cooldownMs: config.providerCooldownMs ??
|
|
8310
|
-
failureWindowMs: config.circuitBreakerWindowMs ??
|
|
8311
|
-
// 5 min default
|
|
11705
|
+
cooldownMs: config.providerCooldownMs ?? TIMEOUTS.PROVIDER_COOLDOWN,
|
|
11706
|
+
failureWindowMs: config.circuitBreakerWindowMs ?? TIMEOUTS.CIRCUIT_BREAKER_WINDOW
|
|
8312
11707
|
});
|
|
8313
11708
|
void (async () => {
|
|
8314
11709
|
try {
|
|
@@ -8352,6 +11747,13 @@ var Router = class {
|
|
|
8352
11747
|
}
|
|
8353
11748
|
}
|
|
8354
11749
|
}
|
|
11750
|
+
/**
|
|
11751
|
+
* Get the number of configured providers.
|
|
11752
|
+
* @returns Number of providers registered with this router
|
|
11753
|
+
*/
|
|
11754
|
+
get providerCount() {
|
|
11755
|
+
return this.providers.length;
|
|
11756
|
+
}
|
|
8355
11757
|
/**
|
|
8356
11758
|
* Warm up provider availability caches immediately.
|
|
8357
11759
|
* Phase 3 (v5.6.3): Eliminates cold-start latency on first request.
|
|
@@ -10337,9 +13739,9 @@ var MemoryManager = class _MemoryManager {
|
|
|
10337
13739
|
throw new MemoryError("Memory manager not initialized", "DATABASE_ERROR");
|
|
10338
13740
|
}
|
|
10339
13741
|
try {
|
|
10340
|
-
const { mkdir:
|
|
13742
|
+
const { mkdir: mkdir4 } = await import('fs/promises');
|
|
10341
13743
|
const destDir = dirname3(destPath);
|
|
10342
|
-
await
|
|
13744
|
+
await mkdir4(destDir, { recursive: true });
|
|
10343
13745
|
await this.db.backup(destPath);
|
|
10344
13746
|
logger.info("Database backup created", { destPath: normalizePath(destPath) });
|
|
10345
13747
|
} catch (error) {
|
|
@@ -10787,7 +14189,7 @@ var LazyMemoryManager = class {
|
|
|
10787
14189
|
/**
|
|
10788
14190
|
* Backup database to destination path (lazy initialization on first call)
|
|
10789
14191
|
*/
|
|
10790
|
-
async backup(destPath,
|
|
14192
|
+
async backup(destPath, _onProgress) {
|
|
10791
14193
|
const manager = await this.ensureInitialized();
|
|
10792
14194
|
return manager.backup(destPath);
|
|
10793
14195
|
}
|
|
@@ -11681,12 +15083,13 @@ var SessionManager = class _SessionManager {
|
|
|
11681
15083
|
*/
|
|
11682
15084
|
async getStats() {
|
|
11683
15085
|
const sessions = Array.from(this.activeSessions.values());
|
|
11684
|
-
|
|
11685
|
-
|
|
11686
|
-
|
|
11687
|
-
|
|
11688
|
-
|
|
11689
|
-
}
|
|
15086
|
+
let active = 0, completed = 0, failed = 0;
|
|
15087
|
+
for (const s of sessions) {
|
|
15088
|
+
if (s.status === "active") active++;
|
|
15089
|
+
else if (s.status === "completed") completed++;
|
|
15090
|
+
else if (s.status === "failed") failed++;
|
|
15091
|
+
}
|
|
15092
|
+
return { total: sessions.length, active, completed, failed };
|
|
11690
15093
|
}
|
|
11691
15094
|
/**
|
|
11692
15095
|
* Load sessions from persistence file
|
|
@@ -12967,11 +16370,13 @@ var ContextManager = class {
|
|
|
12967
16370
|
* Cleanup context
|
|
12968
16371
|
*
|
|
12969
16372
|
* v5.2: No cleanup needed - agent workspaces no longer created
|
|
16373
|
+
* Note: Returns Promise for interface compatibility
|
|
12970
16374
|
*/
|
|
12971
|
-
|
|
16375
|
+
cleanup(context) {
|
|
12972
16376
|
logger.debug("Context cleanup (no-op in v5.2)", {
|
|
12973
16377
|
agent: context.agent.name
|
|
12974
16378
|
});
|
|
16379
|
+
return Promise.resolve();
|
|
12975
16380
|
}
|
|
12976
16381
|
};
|
|
12977
16382
|
|
|
@@ -12995,6 +16400,7 @@ var AgentNotFoundError = class extends Error {
|
|
|
12995
16400
|
|
|
12996
16401
|
// src/agents/profile-loader.ts
|
|
12997
16402
|
init_logger();
|
|
16403
|
+
init_validation_limits();
|
|
12998
16404
|
|
|
12999
16405
|
// src/agents/agent-schemas.ts
|
|
13000
16406
|
init_esm_shims();
|
|
@@ -13395,7 +16801,7 @@ var ProfileLoader = class {
|
|
|
13395
16801
|
const files = await readdir(this.fallbackProfilesDir);
|
|
13396
16802
|
const profiles = files.filter((file) => extname$1(file) === ".yaml" || extname$1(file) === ".yml").map((file) => basename(file, extname$1(file)));
|
|
13397
16803
|
profiles.forEach((p) => profileSet.add(p));
|
|
13398
|
-
} catch (
|
|
16804
|
+
} catch (_error) {
|
|
13399
16805
|
logger.debug("Fallback profiles directory not found", {
|
|
13400
16806
|
dir: this.fallbackProfilesDir
|
|
13401
16807
|
});
|
|
@@ -13672,7 +17078,7 @@ var ProfileLoader = class {
|
|
|
13672
17078
|
* Protected by mutex to prevent race conditions
|
|
13673
17079
|
*/
|
|
13674
17080
|
async clearCache() {
|
|
13675
|
-
await this.initMutex.runExclusive(
|
|
17081
|
+
await this.initMutex.runExclusive(() => {
|
|
13676
17082
|
this.cache.clear();
|
|
13677
17083
|
this.displayNameMap.clear();
|
|
13678
17084
|
this.mapInitialized = false;
|
|
@@ -14447,7 +17853,7 @@ var DelegationParser = class _DelegationParser {
|
|
|
14447
17853
|
await this.extractMatches(_DelegationParser.DELEGATION_PATTERNS.chineseRequest, response, fromAgent, delegations, "\u8ACB", resolvedNameCache);
|
|
14448
17854
|
await this.extractMatches(_DelegationParser.DELEGATION_PATTERNS.chineseFormal, response, fromAgent, delegations, "\u59D4\u6D3E\u7D66", resolvedNameCache);
|
|
14449
17855
|
delegations.sort((a, b) => a.position - b.position);
|
|
14450
|
-
const result = delegations.map(({ position, ...rest }) => rest);
|
|
17856
|
+
const result = delegations.map(({ position: _position, ...rest }) => rest);
|
|
14451
17857
|
logger.info(`Parsed ${delegations.length} delegation(s)`, {
|
|
14452
17858
|
fromAgent,
|
|
14453
17859
|
delegations: result.map((d) => ({ toAgent: d.toAgent, taskPreview: d.task.substring(0, 50) }))
|
|
@@ -15487,7 +18893,7 @@ var ParallelAgentExecutor = class {
|
|
|
15487
18893
|
/**
|
|
15488
18894
|
* Execute a batch of agents in parallel
|
|
15489
18895
|
*/
|
|
15490
|
-
async executeBatchParallel(batch, graph, context, options, results, timeline,
|
|
18896
|
+
async executeBatchParallel(batch, graph, context, options, results, timeline, _executionStartTime) {
|
|
15491
18897
|
logger.info("Executing batch in parallel", { agents: batch });
|
|
15492
18898
|
const promises = batch.map(async (agentName) => {
|
|
15493
18899
|
const node = graph.nodes.get(agentName);
|
|
@@ -15721,7 +19127,7 @@ var ParallelAgentExecutor = class {
|
|
|
15721
19127
|
/**
|
|
15722
19128
|
* Mark batch as cancelled
|
|
15723
19129
|
*/
|
|
15724
|
-
markBatchAsCancelled(batch, graph, timeline,
|
|
19130
|
+
markBatchAsCancelled(batch, graph, timeline, _executionStartTime) {
|
|
15725
19131
|
const now = Date.now();
|
|
15726
19132
|
for (const agentName of batch) {
|
|
15727
19133
|
const node = graph.nodes.get(agentName);
|
|
@@ -15861,6 +19267,9 @@ var ParallelAgentExecutor = class {
|
|
|
15861
19267
|
};
|
|
15862
19268
|
|
|
15863
19269
|
// src/agents/executor.ts
|
|
19270
|
+
function isErrorLike(error) {
|
|
19271
|
+
return typeof error === "object" && error !== null;
|
|
19272
|
+
}
|
|
15864
19273
|
var AgentExecutor = class {
|
|
15865
19274
|
sessionManager;
|
|
15866
19275
|
workspaceManager;
|
|
@@ -15990,7 +19399,7 @@ Retry attempt ${attempt}/${maxAttempts}...`));
|
|
|
15990
19399
|
}
|
|
15991
19400
|
return await this.executeInternal(context, options);
|
|
15992
19401
|
} catch (error) {
|
|
15993
|
-
lastError = error;
|
|
19402
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
15994
19403
|
const isRetryable = this.isRetryableError(error, retryableErrors);
|
|
15995
19404
|
if (!isRetryable || attempt >= maxAttempts) {
|
|
15996
19405
|
throw error;
|
|
@@ -16000,7 +19409,8 @@ Retry attempt ${attempt}/${maxAttempts}...`));
|
|
|
16000
19409
|
maxDelay
|
|
16001
19410
|
);
|
|
16002
19411
|
if (verbose) {
|
|
16003
|
-
|
|
19412
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
19413
|
+
console.log(chalk4.yellow(`Retryable error occurred: ${errorMessage}`));
|
|
16004
19414
|
console.log(chalk4.gray(`Waiting ${delay}ms before retry...`));
|
|
16005
19415
|
}
|
|
16006
19416
|
await this.sleep(delay, signal);
|
|
@@ -16015,7 +19425,7 @@ Retry attempt ${attempt}/${maxAttempts}...`));
|
|
|
16015
19425
|
*/
|
|
16016
19426
|
async executeWithTimeout(context, options) {
|
|
16017
19427
|
const timeout = options.timeout;
|
|
16018
|
-
const { verbose = false } = options;
|
|
19428
|
+
const { verbose: _verbose = false } = options;
|
|
16019
19429
|
let timeoutId = null;
|
|
16020
19430
|
const controller = new AbortController();
|
|
16021
19431
|
const executionOptions = {
|
|
@@ -16713,7 +20123,12 @@ ${context.task}`;
|
|
|
16713
20123
|
*/
|
|
16714
20124
|
isRetryableError(error, retryableErrors) {
|
|
16715
20125
|
const patterns = retryableErrors || this.defaultRetryConfig.retryableErrors;
|
|
16716
|
-
|
|
20126
|
+
let errorString = "";
|
|
20127
|
+
if (isErrorLike(error)) {
|
|
20128
|
+
errorString = (error.message || error.code || "").toLowerCase();
|
|
20129
|
+
} else if (typeof error === "string") {
|
|
20130
|
+
errorString = error.toLowerCase();
|
|
20131
|
+
}
|
|
16717
20132
|
return patterns.some(
|
|
16718
20133
|
(pattern) => errorString.includes(pattern.toLowerCase())
|
|
16719
20134
|
);
|
|
@@ -18141,7 +21556,7 @@ function categorizeTools(name) {
|
|
|
18141
21556
|
if (name.includes("context")) return "context";
|
|
18142
21557
|
return "execution";
|
|
18143
21558
|
}
|
|
18144
|
-
function getExecutionMode(providerName,
|
|
21559
|
+
function getExecutionMode(providerName, _providerConfig) {
|
|
18145
21560
|
if (providerName === "glm" || providerName === "grok") {
|
|
18146
21561
|
return "sdk";
|
|
18147
21562
|
}
|
|
@@ -18600,13 +22015,14 @@ function compressWithInfo(payload, options = {}) {
|
|
|
18600
22015
|
init_esm_shims();
|
|
18601
22016
|
init_factory();
|
|
18602
22017
|
init_logger();
|
|
22018
|
+
init_validation_limits();
|
|
18603
22019
|
var NATIVE_MODULE_ERROR_PATTERNS = [
|
|
18604
22020
|
"NODE_MODULE_VERSION",
|
|
18605
22021
|
"was compiled against a different Node.js version",
|
|
18606
22022
|
"better_sqlite3.node"
|
|
18607
22023
|
];
|
|
18608
22024
|
var DEFAULT_CONFIG3 = {
|
|
18609
|
-
dbPath:
|
|
22025
|
+
dbPath: `${AX_PATHS.TASKS}/tasks.db`,
|
|
18610
22026
|
maxPayloadBytes: 1024 * 1024,
|
|
18611
22027
|
// 1MB
|
|
18612
22028
|
compressionEnabled: true,
|
|
@@ -18614,7 +22030,7 @@ var DEFAULT_CONFIG3 = {
|
|
|
18614
22030
|
defaultTtlHours: 24,
|
|
18615
22031
|
maxTtlHours: 168,
|
|
18616
22032
|
// 7 days
|
|
18617
|
-
busyTimeout:
|
|
22033
|
+
busyTimeout: DATABASE.BUSY_TIMEOUT
|
|
18618
22034
|
};
|
|
18619
22035
|
var SQL = {
|
|
18620
22036
|
CREATE_TABLE: `
|
|
@@ -20133,7 +23549,6 @@ function createRunTaskHandler(deps) {
|
|
|
20133
23549
|
});
|
|
20134
23550
|
try {
|
|
20135
23551
|
const taskEngine = getTaskEngine();
|
|
20136
|
-
const session = deps.getSession();
|
|
20137
23552
|
const options = {
|
|
20138
23553
|
engineOverride: input.engine_override,
|
|
20139
23554
|
timeoutMs: input.timeout_ms,
|
|
@@ -20627,7 +24042,11 @@ var McpClient = class _McpClient extends EventEmitter {
|
|
|
20627
24042
|
const params = {
|
|
20628
24043
|
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
20629
24044
|
capabilities: {
|
|
20630
|
-
tools: {}
|
|
24045
|
+
tools: {},
|
|
24046
|
+
resources: {},
|
|
24047
|
+
prompts: {},
|
|
24048
|
+
resourceTemplates: {},
|
|
24049
|
+
experimental: {}
|
|
20631
24050
|
},
|
|
20632
24051
|
clientInfo: {
|
|
20633
24052
|
name: "automatosx",
|
|
@@ -22386,7 +25805,6 @@ var CodexEventNormalizer = class extends BaseEventNormalizer {
|
|
|
22386
25805
|
const inputTokens = event.inputTokens ?? 0;
|
|
22387
25806
|
const outputTokens = event.outputTokens ?? 0;
|
|
22388
25807
|
this.tokenCount = inputTokens + outputTokens;
|
|
22389
|
-
Date.now() - this.startTime;
|
|
22390
25808
|
return this.createEvent("execution.progress", {
|
|
22391
25809
|
agent: "codex",
|
|
22392
25810
|
progress: 100,
|
|
@@ -22433,6 +25851,93 @@ var CodexEventNormalizer = class extends BaseEventNormalizer {
|
|
|
22433
25851
|
}
|
|
22434
25852
|
};
|
|
22435
25853
|
|
|
25854
|
+
// src/mcp/resource-templates.ts
|
|
25855
|
+
init_esm_shims();
|
|
25856
|
+
var AGENT_TEMPLATE_NAME = "agent_profile";
|
|
25857
|
+
var AGENT_URI_TEMPLATE = "agent/{agent}";
|
|
25858
|
+
var WORKSPACE_PRD_TEMPLATE_NAME = "workspace_prd_file";
|
|
25859
|
+
var WORKSPACE_PRD_URI_TEMPLATE = "workspace/prd/{path}";
|
|
25860
|
+
var WORKSPACE_TMP_TEMPLATE_NAME = "workspace_tmp_file";
|
|
25861
|
+
var WORKSPACE_TMP_URI_TEMPLATE = "workspace/tmp/{path}";
|
|
25862
|
+
var RESOURCE_TEMPLATES = [
|
|
25863
|
+
{
|
|
25864
|
+
name: AGENT_TEMPLATE_NAME,
|
|
25865
|
+
uriTemplate: AGENT_URI_TEMPLATE,
|
|
25866
|
+
description: "Render an AutomatosX agent profile by name",
|
|
25867
|
+
mimeType: "text/markdown",
|
|
25868
|
+
variableDefinitions: [
|
|
25869
|
+
{ name: "agent", description: "Agent name", required: true }
|
|
25870
|
+
]
|
|
25871
|
+
},
|
|
25872
|
+
{
|
|
25873
|
+
name: WORKSPACE_PRD_TEMPLATE_NAME,
|
|
25874
|
+
uriTemplate: WORKSPACE_PRD_URI_TEMPLATE,
|
|
25875
|
+
description: "Read a PRD workspace file (automatosx/PRD)",
|
|
25876
|
+
mimeType: "text/markdown",
|
|
25877
|
+
variableDefinitions: [
|
|
25878
|
+
{ name: "path", description: "Relative path under automatosx/PRD", required: true }
|
|
25879
|
+
]
|
|
25880
|
+
},
|
|
25881
|
+
{
|
|
25882
|
+
name: WORKSPACE_TMP_TEMPLATE_NAME,
|
|
25883
|
+
uriTemplate: WORKSPACE_TMP_URI_TEMPLATE,
|
|
25884
|
+
description: "Read a temporary workspace file (automatosx/tmp)",
|
|
25885
|
+
mimeType: "text/markdown",
|
|
25886
|
+
variableDefinitions: [
|
|
25887
|
+
{ name: "path", description: "Relative path under automatosx/tmp", required: true }
|
|
25888
|
+
]
|
|
25889
|
+
}
|
|
25890
|
+
];
|
|
25891
|
+
function listResourceTemplates() {
|
|
25892
|
+
return RESOURCE_TEMPLATES;
|
|
25893
|
+
}
|
|
25894
|
+
async function resolveResourceTemplate(uri, variables, profileLoader, workspaceManager) {
|
|
25895
|
+
if (uri === AGENT_URI_TEMPLATE) {
|
|
25896
|
+
const agent = variables?.agent;
|
|
25897
|
+
if (!agent) {
|
|
25898
|
+
throw new Error("Missing required variable: agent");
|
|
25899
|
+
}
|
|
25900
|
+
const profile = await profileLoader.loadProfile(agent);
|
|
25901
|
+
const summary = [
|
|
25902
|
+
`# ${agent}`,
|
|
25903
|
+
profile.role ? `**Role:** ${profile.role}` : "",
|
|
25904
|
+
profile.abilities?.length ? `**Abilities:** ${profile.abilities.join(", ")}` : "",
|
|
25905
|
+
"",
|
|
25906
|
+
profile.systemPrompt || "No system prompt defined."
|
|
25907
|
+
].filter(Boolean).join("\n");
|
|
25908
|
+
return {
|
|
25909
|
+
uri: `agent/${agent}`,
|
|
25910
|
+
name: `Agent: ${agent}`,
|
|
25911
|
+
description: `AutomatosX agent profile for ${agent}`,
|
|
25912
|
+
mimeType: "text/markdown",
|
|
25913
|
+
contents: [
|
|
25914
|
+
{ type: "text", text: summary },
|
|
25915
|
+
{ type: "application/json", json: profile }
|
|
25916
|
+
]
|
|
25917
|
+
};
|
|
25918
|
+
}
|
|
25919
|
+
if (uri === WORKSPACE_PRD_URI_TEMPLATE || uri === WORKSPACE_TMP_URI_TEMPLATE) {
|
|
25920
|
+
const path7 = variables?.path;
|
|
25921
|
+
if (!path7) {
|
|
25922
|
+
throw new Error("Missing required variable: path");
|
|
25923
|
+
}
|
|
25924
|
+
const isPrd = uri === WORKSPACE_PRD_URI_TEMPLATE;
|
|
25925
|
+
const readFn = isPrd ? workspaceManager.readPRD.bind(workspaceManager) : workspaceManager.readTmp.bind(workspaceManager);
|
|
25926
|
+
const content = await readFn(path7);
|
|
25927
|
+
return {
|
|
25928
|
+
uri: `${isPrd ? "prd" : "tmp"}/${path7}`,
|
|
25929
|
+
name: `${isPrd ? "PRD" : "Tmp"}: ${path7}`,
|
|
25930
|
+
description: `Workspace ${isPrd ? "PRD" : "tmp"} file`,
|
|
25931
|
+
mimeType: "text/markdown",
|
|
25932
|
+
contents: [
|
|
25933
|
+
{ type: "text", text: content },
|
|
25934
|
+
{ type: "application/json", json: { path: path7, content, workspace: isPrd ? "PRD" : "tmp" } }
|
|
25935
|
+
]
|
|
25936
|
+
};
|
|
25937
|
+
}
|
|
25938
|
+
throw new Error(`Unknown resource template: ${uri}`);
|
|
25939
|
+
}
|
|
25940
|
+
|
|
22436
25941
|
// src/mcp/server.ts
|
|
22437
25942
|
var CLIENT_PATTERNS = [
|
|
22438
25943
|
[["claude"], "claude"],
|
|
@@ -22468,6 +25973,8 @@ var McpServer = class _McpServer {
|
|
|
22468
25973
|
// Track client-initiated cancellations
|
|
22469
25974
|
requestControllers = /* @__PURE__ */ new Map();
|
|
22470
25975
|
// Abort long-running handlers
|
|
25976
|
+
toolAllowlist;
|
|
25977
|
+
authToken;
|
|
22471
25978
|
// v10.5.0: MCP Session for Smart Routing
|
|
22472
25979
|
session = null;
|
|
22473
25980
|
// v10.6.0: MCP Client Pool for cross-provider execution
|
|
@@ -22476,6 +25983,7 @@ var McpServer = class _McpServer {
|
|
|
22476
25983
|
eventBridge = null;
|
|
22477
25984
|
streamingNotifier = null;
|
|
22478
25985
|
enableStreamingNotifications = true;
|
|
25986
|
+
negotiatedProtocolVersion = MCP_PROTOCOL_VERSION;
|
|
22479
25987
|
// Shared services (initialized once per server)
|
|
22480
25988
|
router;
|
|
22481
25989
|
memoryManager;
|
|
@@ -22489,6 +25997,12 @@ var McpServer = class _McpServer {
|
|
|
22489
25997
|
if (options.debug) {
|
|
22490
25998
|
setLogLevel("debug");
|
|
22491
25999
|
}
|
|
26000
|
+
if (options.toolAllowlist?.length) {
|
|
26001
|
+
this.toolAllowlist = new Set(options.toolAllowlist);
|
|
26002
|
+
}
|
|
26003
|
+
if (options.authToken) {
|
|
26004
|
+
this.authToken = options.authToken;
|
|
26005
|
+
}
|
|
22492
26006
|
this.enableStreamingNotifications = options.enableStreamingNotifications ?? true;
|
|
22493
26007
|
this.version = getVersion();
|
|
22494
26008
|
this.ajv = new Ajv({ allErrors: true });
|
|
@@ -22498,6 +26012,24 @@ var McpServer = class _McpServer {
|
|
|
22498
26012
|
streamingNotifications: this.enableStreamingNotifications
|
|
22499
26013
|
});
|
|
22500
26014
|
}
|
|
26015
|
+
/** Determine if negotiated protocol is v2 */
|
|
26016
|
+
isV2Protocol() {
|
|
26017
|
+
return this.negotiatedProtocolVersion === MCP_SUPPORTED_VERSIONS[0];
|
|
26018
|
+
}
|
|
26019
|
+
/** Build capability set based on negotiated protocol */
|
|
26020
|
+
buildCapabilities() {
|
|
26021
|
+
const base = { tools: {} };
|
|
26022
|
+
if (this.isV2Protocol()) {
|
|
26023
|
+
return {
|
|
26024
|
+
...base,
|
|
26025
|
+
resources: {},
|
|
26026
|
+
prompts: {},
|
|
26027
|
+
resourceTemplates: {},
|
|
26028
|
+
experimental: {}
|
|
26029
|
+
};
|
|
26030
|
+
}
|
|
26031
|
+
return base;
|
|
26032
|
+
}
|
|
22501
26033
|
/**
|
|
22502
26034
|
* Get static tool schemas (no initialization required)
|
|
22503
26035
|
* Returns tool schemas that can be provided during MCP handshake
|
|
@@ -22657,36 +26189,61 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22657
26189
|
const projectDir = process.cwd();
|
|
22658
26190
|
const config = await loadConfig(projectDir);
|
|
22659
26191
|
const teamManager = new TeamManager(
|
|
22660
|
-
join(projectDir,
|
|
26192
|
+
join(projectDir, AX_PATHS.TEAMS)
|
|
22661
26193
|
);
|
|
22662
26194
|
this.profileLoader = new ProfileLoader(
|
|
22663
|
-
join(projectDir,
|
|
26195
|
+
join(projectDir, AX_PATHS.AGENTS),
|
|
22664
26196
|
void 0,
|
|
22665
26197
|
teamManager
|
|
22666
26198
|
);
|
|
22667
26199
|
const abilitiesManager = new AbilitiesManager(
|
|
22668
|
-
join(projectDir,
|
|
26200
|
+
join(projectDir, AX_PATHS.ABILITIES)
|
|
22669
26201
|
);
|
|
22670
26202
|
this.memoryManager = new LazyMemoryManager({
|
|
22671
|
-
dbPath: join(projectDir,
|
|
26203
|
+
dbPath: join(projectDir, AX_PATHS.MEMORY, "memory.db")
|
|
22672
26204
|
});
|
|
22673
26205
|
this.pathResolver = new PathResolver({
|
|
22674
26206
|
projectDir,
|
|
22675
26207
|
workingDir: process.cwd(),
|
|
22676
|
-
agentWorkspace: join(projectDir,
|
|
26208
|
+
agentWorkspace: join(projectDir, AX_PATHS.WORKSPACES)
|
|
22677
26209
|
});
|
|
22678
26210
|
const providers = [];
|
|
22679
26211
|
if (config.providers["claude-code"]?.enabled) {
|
|
22680
26212
|
const { ClaudeProvider: ClaudeProvider2 } = await Promise.resolve().then(() => (init_claude_provider(), claude_provider_exports));
|
|
22681
|
-
|
|
26213
|
+
const claudeConfig = config.providers["claude-code"];
|
|
26214
|
+
providers.push(new ClaudeProvider2({ ...claudeConfig, name: "claude-code", command: claudeConfig.command || "claude" }));
|
|
22682
26215
|
}
|
|
22683
26216
|
if (config.providers["gemini-cli"]?.enabled) {
|
|
22684
26217
|
const { GeminiProvider: GeminiProvider2 } = await Promise.resolve().then(() => (init_gemini_provider(), gemini_provider_exports));
|
|
22685
|
-
|
|
26218
|
+
const geminiConfig = config.providers["gemini-cli"];
|
|
26219
|
+
providers.push(new GeminiProvider2({ ...geminiConfig, name: "gemini-cli", command: geminiConfig.command || "gemini" }));
|
|
22686
26220
|
}
|
|
22687
26221
|
if (config.providers["openai"]?.enabled) {
|
|
22688
26222
|
const { createOpenAIProviderSync: createOpenAIProviderSync2 } = await Promise.resolve().then(() => (init_openai_provider_factory(), openai_provider_factory_exports));
|
|
22689
|
-
|
|
26223
|
+
const openaiConfig = config.providers["openai"];
|
|
26224
|
+
providers.push(createOpenAIProviderSync2({ ...openaiConfig, name: "openai", command: openaiConfig.command || "codex" }, openaiConfig.integration));
|
|
26225
|
+
}
|
|
26226
|
+
if (config.providers["glm"]?.enabled) {
|
|
26227
|
+
const { GLMProvider: GLMProvider2 } = await Promise.resolve().then(() => (init_glm_provider(), glm_provider_exports));
|
|
26228
|
+
const glmConfig = config.providers["glm"];
|
|
26229
|
+
providers.push(new GLMProvider2({
|
|
26230
|
+
name: "glm",
|
|
26231
|
+
enabled: true,
|
|
26232
|
+
priority: glmConfig.priority,
|
|
26233
|
+
timeout: glmConfig.timeout,
|
|
26234
|
+
mode: "sdk"
|
|
26235
|
+
}));
|
|
26236
|
+
}
|
|
26237
|
+
if (config.providers["grok"]?.enabled) {
|
|
26238
|
+
const { GrokProvider: GrokProvider2 } = await Promise.resolve().then(() => (init_grok_provider(), grok_provider_exports));
|
|
26239
|
+
const grokConfig = config.providers["grok"];
|
|
26240
|
+
providers.push(new GrokProvider2({
|
|
26241
|
+
name: "grok",
|
|
26242
|
+
enabled: true,
|
|
26243
|
+
priority: grokConfig.priority,
|
|
26244
|
+
timeout: grokConfig.timeout,
|
|
26245
|
+
mode: "sdk"
|
|
26246
|
+
}));
|
|
22690
26247
|
}
|
|
22691
26248
|
const healthCheckInterval = config.router?.healthCheckInterval;
|
|
22692
26249
|
this.router = new Router({
|
|
@@ -22840,9 +26397,7 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22840
26397
|
register("create_task", createCreateTaskHandler({
|
|
22841
26398
|
getSession: () => this.session
|
|
22842
26399
|
}));
|
|
22843
|
-
register("run_task", createRunTaskHandler(
|
|
22844
|
-
getSession: () => this.session
|
|
22845
|
-
}));
|
|
26400
|
+
register("run_task", createRunTaskHandler());
|
|
22846
26401
|
register("get_task_result", createGetTaskResultHandler());
|
|
22847
26402
|
register("list_tasks", createListTasksHandler());
|
|
22848
26403
|
register("delete_task", createDeleteTaskHandler());
|
|
@@ -22877,6 +26432,14 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22877
26432
|
return await this.handleResourcesList(request, responseId);
|
|
22878
26433
|
case "resources/read":
|
|
22879
26434
|
return await this.handleResourceRead(request, responseId);
|
|
26435
|
+
case "resources/templates/list":
|
|
26436
|
+
return await this.handleResourceTemplatesList(request, responseId);
|
|
26437
|
+
case "resources/templates/read":
|
|
26438
|
+
return await this.handleResourceTemplateRead(request, responseId);
|
|
26439
|
+
case "prompts/list":
|
|
26440
|
+
return await this.handlePromptsList(request, responseId);
|
|
26441
|
+
case "prompts/get":
|
|
26442
|
+
return await this.handlePromptGet(request, responseId);
|
|
22880
26443
|
case "$/cancelRequest":
|
|
22881
26444
|
return this.handleCancelRequest(request, responseId);
|
|
22882
26445
|
default:
|
|
@@ -22915,9 +26478,12 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22915
26478
|
clientName: clientInfo.name,
|
|
22916
26479
|
normalizedProvider: this.session.normalizedProvider
|
|
22917
26480
|
});
|
|
26481
|
+
const requestedProtocol = request.params?.protocolVersion;
|
|
26482
|
+
const negotiated = MCP_SUPPORTED_VERSIONS.find((version) => version === requestedProtocol) ?? "2024-11-05";
|
|
26483
|
+
this.negotiatedProtocolVersion = negotiated;
|
|
22918
26484
|
const response = {
|
|
22919
|
-
protocolVersion:
|
|
22920
|
-
capabilities:
|
|
26485
|
+
protocolVersion: negotiated,
|
|
26486
|
+
capabilities: this.buildCapabilities(),
|
|
22921
26487
|
serverInfo: { name: "automatosx", version: this.version }
|
|
22922
26488
|
};
|
|
22923
26489
|
logger.info("[MCP Server] Initialize handshake complete (< 1ms)");
|
|
@@ -22931,7 +26497,11 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22931
26497
|
*/
|
|
22932
26498
|
handleToolsList(_request, id) {
|
|
22933
26499
|
logger.debug("[MCP Server] Tools list requested (static schemas)");
|
|
22934
|
-
const tools = _McpServer.getStaticToolSchemas()
|
|
26500
|
+
const tools = _McpServer.getStaticToolSchemas().map((schema) => ({
|
|
26501
|
+
...schema,
|
|
26502
|
+
// If allowlist is set, hide tools not allowed
|
|
26503
|
+
...this.toolAllowlist && !this.toolAllowlist.has(schema.name) ? { hidden: true } : {}
|
|
26504
|
+
}));
|
|
22935
26505
|
return { jsonrpc: "2.0", id, result: { tools } };
|
|
22936
26506
|
}
|
|
22937
26507
|
/**
|
|
@@ -22948,6 +26518,81 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22948
26518
|
}));
|
|
22949
26519
|
return { jsonrpc: "2.0", id, result: { resources } };
|
|
22950
26520
|
}
|
|
26521
|
+
/**
|
|
26522
|
+
* Handle resources/templates/list request (v2 capability)
|
|
26523
|
+
*/
|
|
26524
|
+
async handleResourceTemplatesList(_request, id) {
|
|
26525
|
+
if (!this.isV2Protocol()) {
|
|
26526
|
+
return this.createErrorResponse(id, -32601 /* MethodNotFound */, "resources/templates/list is only available in MCP v2");
|
|
26527
|
+
}
|
|
26528
|
+
await this.ensureInitialized();
|
|
26529
|
+
const resourceTemplates = listResourceTemplates();
|
|
26530
|
+
return { jsonrpc: "2.0", id, result: { resourceTemplates } };
|
|
26531
|
+
}
|
|
26532
|
+
/**
|
|
26533
|
+
* Handle prompts/list request (expose common starter prompts)
|
|
26534
|
+
*/
|
|
26535
|
+
async handlePromptsList(_request, id) {
|
|
26536
|
+
await this.ensureInitialized();
|
|
26537
|
+
const prompts = [
|
|
26538
|
+
{
|
|
26539
|
+
name: "agent_context",
|
|
26540
|
+
description: "Get agent context and system prompt for a given agent name",
|
|
26541
|
+
arguments: [{ name: "agent", required: true, description: "Agent name" }]
|
|
26542
|
+
},
|
|
26543
|
+
{
|
|
26544
|
+
name: "status",
|
|
26545
|
+
description: "Get AutomatosX MCP status summary"
|
|
26546
|
+
}
|
|
26547
|
+
];
|
|
26548
|
+
return { jsonrpc: "2.0", id, result: { prompts } };
|
|
26549
|
+
}
|
|
26550
|
+
/**
|
|
26551
|
+
* Handle prompts/get request
|
|
26552
|
+
*/
|
|
26553
|
+
async handlePromptGet(request, id) {
|
|
26554
|
+
await this.ensureInitialized();
|
|
26555
|
+
const name = request.params?.name;
|
|
26556
|
+
if (!name) {
|
|
26557
|
+
return this.createErrorResponse(id, -32602 /* InvalidParams */, "Prompt name is required");
|
|
26558
|
+
}
|
|
26559
|
+
switch (name) {
|
|
26560
|
+
case "agent_context": {
|
|
26561
|
+
const agent = request.params?.arguments?.agent;
|
|
26562
|
+
if (!agent) {
|
|
26563
|
+
return this.createErrorResponse(id, -32602 /* InvalidParams */, "agent argument is required");
|
|
26564
|
+
}
|
|
26565
|
+
try {
|
|
26566
|
+
const profile = await this.profileLoader.loadProfile(agent);
|
|
26567
|
+
const content = [
|
|
26568
|
+
{ type: "text", text: `System prompt for ${agent}:
|
|
26569
|
+
${profile.systemPrompt || "No system prompt defined."}` },
|
|
26570
|
+
{ type: "application/json", json: profile }
|
|
26571
|
+
];
|
|
26572
|
+
return { jsonrpc: "2.0", id, result: { prompt: { name, description: "Agent context", arguments: [{ name: "agent", required: true }] }, content } };
|
|
26573
|
+
} catch (error) {
|
|
26574
|
+
return this.createErrorResponse(id, -32603 /* InternalError */, `Failed to load agent: ${error.message}`);
|
|
26575
|
+
}
|
|
26576
|
+
}
|
|
26577
|
+
case "status": {
|
|
26578
|
+
const summary = {
|
|
26579
|
+
version: this.version,
|
|
26580
|
+
providerCount: this.router?.providerCount ?? 0,
|
|
26581
|
+
streamingNotifications: this.enableStreamingNotifications
|
|
26582
|
+
};
|
|
26583
|
+
const content = [
|
|
26584
|
+
{ type: "text", text: `AutomatosX MCP status:
|
|
26585
|
+
Version: ${summary.version}
|
|
26586
|
+
Providers: ${summary.providerCount}
|
|
26587
|
+
Streaming: ${summary.streamingNotifications}` },
|
|
26588
|
+
{ type: "application/json", json: summary }
|
|
26589
|
+
];
|
|
26590
|
+
return { jsonrpc: "2.0", id, result: { prompt: { name, description: "AutomatosX status" }, content } };
|
|
26591
|
+
}
|
|
26592
|
+
default:
|
|
26593
|
+
return this.createErrorResponse(id, -32601 /* MethodNotFound */, `Prompt not found: ${name}`);
|
|
26594
|
+
}
|
|
26595
|
+
}
|
|
22951
26596
|
/**
|
|
22952
26597
|
* Handle resources/read request
|
|
22953
26598
|
*/
|
|
@@ -22976,6 +26621,30 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
22976
26621
|
return this.createErrorResponse(id, -32603 /* InternalError */, `Failed to read resource: ${error.message}`);
|
|
22977
26622
|
}
|
|
22978
26623
|
}
|
|
26624
|
+
/**
|
|
26625
|
+
* Handle resources/templates/read request (v2 capability)
|
|
26626
|
+
*/
|
|
26627
|
+
async handleResourceTemplateRead(request, id) {
|
|
26628
|
+
if (!this.isV2Protocol()) {
|
|
26629
|
+
return this.createErrorResponse(id, -32601 /* MethodNotFound */, "resources/templates/read is only available in MCP v2");
|
|
26630
|
+
}
|
|
26631
|
+
await this.ensureInitialized();
|
|
26632
|
+
const uri = request.params?.uri;
|
|
26633
|
+
try {
|
|
26634
|
+
if (!uri) {
|
|
26635
|
+
return this.createErrorResponse(id, -32602 /* InvalidParams */, "Missing resource template URI");
|
|
26636
|
+
}
|
|
26637
|
+
const resolved = await resolveResourceTemplate(
|
|
26638
|
+
uri,
|
|
26639
|
+
request.params?.variables,
|
|
26640
|
+
this.profileLoader,
|
|
26641
|
+
this.workspaceManager
|
|
26642
|
+
);
|
|
26643
|
+
return { jsonrpc: "2.0", id, result: resolved };
|
|
26644
|
+
} catch (error) {
|
|
26645
|
+
return this.createErrorResponse(id, -32603 /* InternalError */, `Failed to read resource template: ${error.message}`);
|
|
26646
|
+
}
|
|
26647
|
+
}
|
|
22979
26648
|
/**
|
|
22980
26649
|
* Validate tool input against its JSON schema.
|
|
22981
26650
|
* Returns null if valid, or error message string if invalid.
|
|
@@ -23050,6 +26719,16 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
23050
26719
|
const { name, arguments: args2 } = request.params;
|
|
23051
26720
|
logger.info("[MCP Server] Tool call", { tool: name });
|
|
23052
26721
|
const requestId = id ?? null;
|
|
26722
|
+
if (this.toolAllowlist && !this.toolAllowlist.has(name)) {
|
|
26723
|
+
return this.createErrorResponse(id, -32600 /* InvalidRequest */, `Tool not allowed: ${name}`);
|
|
26724
|
+
}
|
|
26725
|
+
const schema = this.toolSchemas.find((t) => t.name === name);
|
|
26726
|
+
if (schema?.requiresAuth && this.authToken) {
|
|
26727
|
+
const provided = args2?.auth_token;
|
|
26728
|
+
if (provided !== this.authToken) {
|
|
26729
|
+
return this.createErrorResponse(id, -32600 /* InvalidRequest */, "Unauthorized: invalid auth token");
|
|
26730
|
+
}
|
|
26731
|
+
}
|
|
23053
26732
|
const abortController = requestId !== null ? new AbortController() : null;
|
|
23054
26733
|
if (requestId !== null && abortController) {
|
|
23055
26734
|
this.requestControllers.set(requestId, abortController);
|
|
@@ -23113,20 +26792,21 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
23113
26792
|
logger.debug("[MCP Server] Response sent", { id: response.id, length: json.length });
|
|
23114
26793
|
}
|
|
23115
26794
|
/**
|
|
23116
|
-
* Start stdio server with
|
|
26795
|
+
* Start stdio server with hybrid framing support
|
|
23117
26796
|
*
|
|
23118
|
-
* v12.
|
|
23119
|
-
*
|
|
26797
|
+
* v12.3.1: Supports BOTH framing formats for maximum compatibility:
|
|
26798
|
+
* 1. Newline-delimited (official MCP spec, used by Claude Code, @modelcontextprotocol/sdk)
|
|
26799
|
+
* 2. Content-Length (LSP-style, used by ax-glm, ax-grok, some older clients)
|
|
23120
26800
|
*
|
|
23121
|
-
*
|
|
23122
|
-
*
|
|
23123
|
-
* Reference: @modelcontextprotocol/sdk ReadBuffer uses: buffer.indexOf('\n')
|
|
26801
|
+
* Detection: If first line starts with "Content-Length:", use LSP framing.
|
|
26802
|
+
* Otherwise, use newline-delimited framing.
|
|
23124
26803
|
*
|
|
23125
26804
|
* BUG FIX (v9.0.1): Added iteration limit and buffer size checks to prevent infinite loops
|
|
23126
26805
|
*/
|
|
23127
26806
|
async start() {
|
|
23128
26807
|
logger.info("[MCP Server] Starting stdio JSON-RPC server...");
|
|
23129
26808
|
let buffer = "";
|
|
26809
|
+
let detectedFraming = null;
|
|
23130
26810
|
process.stdin.on("data", (chunk) => {
|
|
23131
26811
|
void this.stdinMutex.runExclusive(async () => {
|
|
23132
26812
|
buffer += chunk.toString("utf-8");
|
|
@@ -23138,17 +26818,49 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
23138
26818
|
buffer = "";
|
|
23139
26819
|
return;
|
|
23140
26820
|
}
|
|
26821
|
+
if (detectedFraming === null) {
|
|
26822
|
+
if (buffer.startsWith("Content-Length:")) {
|
|
26823
|
+
detectedFraming = "content-length";
|
|
26824
|
+
logger.info("[MCP Server] Detected Content-Length framing (LSP-style)");
|
|
26825
|
+
} else if (buffer.includes("{")) {
|
|
26826
|
+
detectedFraming = "newline";
|
|
26827
|
+
logger.info("[MCP Server] Detected newline-delimited framing (MCP spec)");
|
|
26828
|
+
}
|
|
26829
|
+
}
|
|
23141
26830
|
let iterations = 0;
|
|
23142
26831
|
while (iterations < STDIO_MAX_ITERATIONS) {
|
|
23143
26832
|
iterations++;
|
|
23144
|
-
|
|
23145
|
-
if (
|
|
23146
|
-
|
|
23147
|
-
|
|
23148
|
-
|
|
26833
|
+
let jsonMessage = null;
|
|
26834
|
+
if (detectedFraming === "content-length") {
|
|
26835
|
+
const headerEnd = buffer.indexOf("\r\n\r\n");
|
|
26836
|
+
if (headerEnd === -1) break;
|
|
26837
|
+
const headers = buffer.slice(0, headerEnd);
|
|
26838
|
+
const contentLengthMatch = headers.match(/Content-Length:\s*(\d+)/i);
|
|
26839
|
+
if (!contentLengthMatch) {
|
|
26840
|
+
const lineEnd = buffer.indexOf("\n");
|
|
26841
|
+
if (lineEnd !== -1) {
|
|
26842
|
+
buffer = buffer.slice(lineEnd + 1);
|
|
26843
|
+
continue;
|
|
26844
|
+
}
|
|
26845
|
+
break;
|
|
26846
|
+
}
|
|
26847
|
+
const contentLength = parseInt(contentLengthMatch[1], 10);
|
|
26848
|
+
const bodyStart = headerEnd + 4;
|
|
26849
|
+
const bodyEnd = bodyStart + contentLength;
|
|
26850
|
+
if (buffer.length < bodyEnd) break;
|
|
26851
|
+
jsonMessage = buffer.slice(bodyStart, bodyEnd);
|
|
26852
|
+
buffer = buffer.slice(bodyEnd);
|
|
26853
|
+
} else {
|
|
26854
|
+
const newlineIndex = buffer.indexOf("\n");
|
|
26855
|
+
if (newlineIndex === -1) break;
|
|
26856
|
+
jsonMessage = buffer.slice(0, newlineIndex);
|
|
26857
|
+
if (jsonMessage.endsWith("\r")) {
|
|
26858
|
+
jsonMessage = jsonMessage.slice(0, -1);
|
|
26859
|
+
}
|
|
26860
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
26861
|
+
if (jsonMessage.trim() === "") continue;
|
|
23149
26862
|
}
|
|
23150
|
-
|
|
23151
|
-
if (jsonMessage.trim() === "") continue;
|
|
26863
|
+
if (!jsonMessage) break;
|
|
23152
26864
|
if (jsonMessage.length > STDIO_MAX_MESSAGE_SIZE) {
|
|
23153
26865
|
logger.error("[MCP Server] Message size exceeded maximum", {
|
|
23154
26866
|
messageSize: jsonMessage.length,
|
|
@@ -23191,7 +26903,7 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
23191
26903
|
process.stdin.on("end", () => shutdown("Server stopped (stdin closed)"));
|
|
23192
26904
|
process.on("SIGINT", () => shutdown("Received SIGINT, shutting down..."));
|
|
23193
26905
|
process.on("SIGTERM", () => shutdown("Received SIGTERM, shutting down..."));
|
|
23194
|
-
logger.info("[MCP Server] Server started successfully (newline-
|
|
26906
|
+
logger.info("[MCP Server] Server started successfully (hybrid framing: newline + Content-Length)");
|
|
23195
26907
|
}
|
|
23196
26908
|
};
|
|
23197
26909
|
|