@okxweb3/a2a-node 0.2.2 → 0.2.3-beta-6f2a8ec2c7-260810184309
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/dist/cli.js +21739 -20400
- package/dist/index.js +2584 -1099
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3200,7 +3200,14 @@ function resolveCommandPath(command, env = process.env, platform = process.platf
|
|
|
3200
3200
|
}
|
|
3201
3201
|
function resolveCodexCommandPath(env = process.env, storedCommand, platform = process.platform, probe = probeCodexCommand) {
|
|
3202
3202
|
if (platform !== "win32") {
|
|
3203
|
-
|
|
3203
|
+
if (storedCommand && commandExists(storedCommand, env, platform)) {
|
|
3204
|
+
return storedCommand;
|
|
3205
|
+
}
|
|
3206
|
+
const onPath = findExecutable("codex", env, platform);
|
|
3207
|
+
if (onPath) {
|
|
3208
|
+
return onPath;
|
|
3209
|
+
}
|
|
3210
|
+
return collectCodexAppBundledCandidates(env, platform).find(isExecutable) ?? null;
|
|
3204
3211
|
}
|
|
3205
3212
|
for (const candidate of collectCodexCommandCandidates(env, storedCommand, platform)) {
|
|
3206
3213
|
const result = probe(candidate, env, platform);
|
|
@@ -3225,6 +3232,9 @@ function collectCodexCommandCandidates(env = process.env, storedCommand, platfor
|
|
|
3225
3232
|
}
|
|
3226
3233
|
if (platform !== "win32") {
|
|
3227
3234
|
add(findExecutable("codex", env, platform));
|
|
3235
|
+
for (const candidate of collectCodexAppBundledCandidates(env, platform).filter(isExecutable)) {
|
|
3236
|
+
add(candidate);
|
|
3237
|
+
}
|
|
3228
3238
|
return candidates;
|
|
3229
3239
|
}
|
|
3230
3240
|
for (const candidate of localAppDataCodexCandidates(env)) {
|
|
@@ -3246,6 +3256,87 @@ function collectCodexCommandCandidates(env = process.env, storedCommand, platfor
|
|
|
3246
3256
|
}
|
|
3247
3257
|
return candidates;
|
|
3248
3258
|
}
|
|
3259
|
+
function collectCodexAppBundledCandidates(env = process.env, platform = process.platform) {
|
|
3260
|
+
if (platform === "win32") {
|
|
3261
|
+
return localAppDataCodexCandidates(env);
|
|
3262
|
+
}
|
|
3263
|
+
const roots = (env[CODEX_APP_DIRS_ENV] ?? "").split(import_node_path10.delimiter).filter(Boolean);
|
|
3264
|
+
if (platform === "darwin") {
|
|
3265
|
+
roots.push("/Applications", (0, import_node_path10.join)(env.HOME || (0, import_node_os5.homedir)(), "Applications"));
|
|
3266
|
+
}
|
|
3267
|
+
const candidates = [];
|
|
3268
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3269
|
+
for (const root of roots) {
|
|
3270
|
+
const candidate = (0, import_node_path10.join)(root, "Codex.app", "Contents", "Resources", "codex");
|
|
3271
|
+
if (seen.has(candidate)) {
|
|
3272
|
+
continue;
|
|
3273
|
+
}
|
|
3274
|
+
seen.add(candidate);
|
|
3275
|
+
candidates.push(candidate);
|
|
3276
|
+
}
|
|
3277
|
+
return candidates;
|
|
3278
|
+
}
|
|
3279
|
+
function resolveCodexCommandCandidates(env = process.env, storedCommand, platform = process.platform) {
|
|
3280
|
+
const candidates = [];
|
|
3281
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3282
|
+
const add = (command, source) => {
|
|
3283
|
+
if (!command || seen.has(command)) {
|
|
3284
|
+
return;
|
|
3285
|
+
}
|
|
3286
|
+
seen.add(command);
|
|
3287
|
+
candidates.push({ command, source });
|
|
3288
|
+
};
|
|
3289
|
+
add(env[CODEX_COMMAND_OVERRIDE_ENV], "explicit_override");
|
|
3290
|
+
const appBundled = new Set(collectCodexAppBundledCandidates(env, platform));
|
|
3291
|
+
for (const command of collectCodexCommandCandidates(env, storedCommand, platform)) {
|
|
3292
|
+
if (storedCommand && command === storedCommand) {
|
|
3293
|
+
add(command, "persisted");
|
|
3294
|
+
continue;
|
|
3295
|
+
}
|
|
3296
|
+
add(command, appBundled.has(command) ? "app_bundled" : "path");
|
|
3297
|
+
}
|
|
3298
|
+
return candidates;
|
|
3299
|
+
}
|
|
3300
|
+
function resolveCodexCommandSource(env = process.env, storedCommand, platform = process.platform, probe = probeCodexCommand) {
|
|
3301
|
+
const override = env[CODEX_COMMAND_OVERRIDE_ENV];
|
|
3302
|
+
if (override) {
|
|
3303
|
+
return commandExists(override, env, platform) ? { command: override, source: "explicit_override" } : null;
|
|
3304
|
+
}
|
|
3305
|
+
const resolved = resolveCodexCommandPath(env, storedCommand, platform, probe);
|
|
3306
|
+
if (!resolved) {
|
|
3307
|
+
return null;
|
|
3308
|
+
}
|
|
3309
|
+
return {
|
|
3310
|
+
command: resolved,
|
|
3311
|
+
source: classifyResolvedCodexCommand(resolved, storedCommand, env, platform)
|
|
3312
|
+
};
|
|
3313
|
+
}
|
|
3314
|
+
function resolveAiProviderCommandWithSource(provider, env = process.env, storedCommand, platform = process.platform) {
|
|
3315
|
+
const override = env[`OKX_A2A_AI_${provider.toUpperCase()}_COMMAND`];
|
|
3316
|
+
if (override) {
|
|
3317
|
+
return { command: override, source: "explicit_override" };
|
|
3318
|
+
}
|
|
3319
|
+
if (provider === "codex") {
|
|
3320
|
+
const codex = resolveCodexCommandPath(env, storedCommand, platform);
|
|
3321
|
+
if (codex) {
|
|
3322
|
+
return { command: codex, source: classifyResolvedCodexCommand(codex, storedCommand, env, platform) };
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
if (storedCommand && commandExists(storedCommand, env, platform)) {
|
|
3326
|
+
return { command: storedCommand, source: "persisted" };
|
|
3327
|
+
}
|
|
3328
|
+
const found = findExecutable(provider, env, platform);
|
|
3329
|
+
return found ? { command: found, source: "path" } : { command: provider, source: null };
|
|
3330
|
+
}
|
|
3331
|
+
function classifyResolvedCodexCommand(resolved, storedCommand, env, platform) {
|
|
3332
|
+
if (storedCommand && resolved === storedCommand) {
|
|
3333
|
+
return "persisted";
|
|
3334
|
+
}
|
|
3335
|
+
if (collectCodexAppBundledCandidates(env, platform).includes(resolved)) {
|
|
3336
|
+
return "app_bundled";
|
|
3337
|
+
}
|
|
3338
|
+
return "path";
|
|
3339
|
+
}
|
|
3249
3340
|
function readAiProviderTimeoutMs(env = process.env) {
|
|
3250
3341
|
const raw = env.OKX_A2A_AI_PROVIDER_TIMEOUT_MS ?? env.OKX_AGENT_TASK_AI_PROVIDER_TIMEOUT_MS;
|
|
3251
3342
|
if (!raw) {
|
|
@@ -3417,7 +3508,7 @@ function isCodexCommand(command) {
|
|
|
3417
3508
|
function isWindowsAppsPath(commandPath) {
|
|
3418
3509
|
return /(?:^|[\\/])WindowsApps(?:[\\/]|$)/i.test(commandPath);
|
|
3419
3510
|
}
|
|
3420
|
-
var import_node_fs7, import_node_os5, import_node_path10, DEFAULT_AI_PROVIDER_TIMEOUT_MS, CODEX_PROBE_TIMEOUT_MS;
|
|
3511
|
+
var import_node_fs7, import_node_os5, import_node_path10, DEFAULT_AI_PROVIDER_TIMEOUT_MS, CODEX_PROBE_TIMEOUT_MS, CODEX_APP_DIRS_ENV, CODEX_COMMAND_OVERRIDE_ENV;
|
|
3421
3512
|
var init_ai_command = __esm({
|
|
3422
3513
|
"src/ai-command.ts"() {
|
|
3423
3514
|
"use strict";
|
|
@@ -3427,918 +3518,238 @@ var init_ai_command = __esm({
|
|
|
3427
3518
|
init_win_spawn();
|
|
3428
3519
|
DEFAULT_AI_PROVIDER_TIMEOUT_MS = null;
|
|
3429
3520
|
CODEX_PROBE_TIMEOUT_MS = 1e4;
|
|
3521
|
+
CODEX_APP_DIRS_ENV = "OKX_A2A_CODEX_APP_DIRS";
|
|
3522
|
+
CODEX_COMMAND_OVERRIDE_ENV = "OKX_A2A_AI_CODEX_COMMAND";
|
|
3430
3523
|
}
|
|
3431
3524
|
});
|
|
3432
3525
|
|
|
3433
|
-
// src/
|
|
3434
|
-
function
|
|
3435
|
-
|
|
3436
|
-
if (normalized === "codex") {
|
|
3437
|
-
return "codex";
|
|
3438
|
-
}
|
|
3439
|
-
if (normalized === "claude" || normalized === "claude-code" || normalized === "claude_code") {
|
|
3440
|
-
return "claude";
|
|
3441
|
-
}
|
|
3442
|
-
if (normalized === "hermes") {
|
|
3443
|
-
return "hermes";
|
|
3444
|
-
}
|
|
3445
|
-
if (normalized === "openclaw") {
|
|
3446
|
-
return "openclaw";
|
|
3447
|
-
}
|
|
3448
|
-
throw new Error(`Unsupported AI provider "${value}". Use one of: ${AI_PROVIDERS.join(", ")}.`);
|
|
3449
|
-
}
|
|
3450
|
-
function detectAiProviders(commandExists2 = commandExists, env = process.env) {
|
|
3451
|
-
const codex = commandExists2(readProviderCommand("codex", env));
|
|
3452
|
-
const claude = commandExists2(readProviderCommand("claude", env));
|
|
3453
|
-
const hermes = commandExists2(readProviderCommand("hermes", env));
|
|
3454
|
-
const openclaw = commandExists2(readProviderCommand("openclaw", env));
|
|
3455
|
-
return {
|
|
3456
|
-
codex,
|
|
3457
|
-
claude,
|
|
3458
|
-
hermes,
|
|
3459
|
-
openclaw,
|
|
3460
|
-
available: [
|
|
3461
|
-
...codex ? ["codex"] : [],
|
|
3462
|
-
...claude ? ["claude"] : [],
|
|
3463
|
-
...hermes ? ["hermes"] : [],
|
|
3464
|
-
...openclaw ? ["openclaw"] : []
|
|
3465
|
-
]
|
|
3466
|
-
};
|
|
3526
|
+
// src/codex-readiness.ts
|
|
3527
|
+
function codexReadinessCacheKey(env = process.env, persistOverrideCommand = false) {
|
|
3528
|
+
return `codex|${env[CODEX_COMMAND_OVERRIDE_ENV2] ?? ""}${persistOverrideCommand ? "|persist-override" : ""}`;
|
|
3467
3529
|
}
|
|
3468
|
-
function
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
return normalizeAiProvider(explicit);
|
|
3472
|
-
}
|
|
3473
|
-
const runtime = detectRuntime(env);
|
|
3474
|
-
return runtime === "unknown" ? null : runtime;
|
|
3475
|
-
}
|
|
3476
|
-
function hasAiRuntimeMarker(env = process.env) {
|
|
3477
|
-
return !!(detectGatewayInvocation(env) || env.CODEX_THREAD_ID || isTruthyRuntimeMarker(env.CODEX_SHELL) || env.CODEX_MANAGED_BY_NPM || isTruthyRuntimeMarker(env.CODEX_CI) || env.CLAUDE_SESSION_ID || isTruthyRuntimeMarker(env.CLAUDECODE) || env.CLAUDE_CODE_SESSION_ID || env.CLAUDE_PLUGIN_DATA || env.HERMES_SESSION_ID && !env.HERMES_DESKTOP_CWD || env.HERMES_SESSION_KEY || env.HERMES_FLOW_ID || env.OPENCLAW_SHELL || env.OPENCLAW_CLI);
|
|
3478
|
-
}
|
|
3479
|
-
function detectRuntime(env = process.env, options = {}) {
|
|
3480
|
-
const gatewayInvocation = detectGatewayInvocation(env);
|
|
3481
|
-
const strongMatches = [
|
|
3482
|
-
...gatewayInvocation ? [gatewayInvocation] : [],
|
|
3483
|
-
...env.CODEX_THREAD_ID || isTruthyRuntimeMarker(env.CODEX_SHELL) || env.CODEX_MANAGED_BY_NPM || isTruthyRuntimeMarker(env.CODEX_CI) ? ["codex"] : [],
|
|
3484
|
-
...env.CLAUDE_SESSION_ID || isTruthyRuntimeMarker(env.CLAUDECODE) || env.CLAUDE_CODE_SESSION_ID || env.CLAUDE_PLUGIN_DATA ? ["claude"] : [],
|
|
3485
|
-
...env.HERMES_SESSION_ID && !env.HERMES_DESKTOP_CWD || env.HERMES_SESSION_KEY || env.HERMES_FLOW_ID ? ["hermes"] : [],
|
|
3486
|
-
...env.OPENCLAW_SHELL || env.OPENCLAW_CLI ? ["openclaw"] : [],
|
|
3487
|
-
...hasOpenClawParentProcess(options.parentPid ?? process.ppid, options.readParentProcessCommand ?? readParentProcessCommand) ? ["openclaw"] : []
|
|
3488
|
-
];
|
|
3489
|
-
const uniqueMatches = [...new Set(strongMatches)];
|
|
3490
|
-
if (uniqueMatches.length === 1) {
|
|
3491
|
-
return uniqueMatches[0];
|
|
3530
|
+
function buildCodexRecoveryGuidance(state, commandSource = null) {
|
|
3531
|
+
if (state === "not_authenticated" && commandSource === "app_bundled") {
|
|
3532
|
+
return { recoveryAction: "run_login", recoveryGuidance: CODEX_APP_BUNDLED_LOGIN_GUIDANCE };
|
|
3492
3533
|
}
|
|
3493
|
-
return
|
|
3534
|
+
return { ...CODEX_RECOVERY_BY_STATE[state] };
|
|
3494
3535
|
}
|
|
3495
|
-
function
|
|
3496
|
-
const
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
}
|
|
3500
|
-
if (context === "hermes-gateway" || context === "gateway:hermes") {
|
|
3501
|
-
return "hermes";
|
|
3502
|
-
}
|
|
3503
|
-
if (isTruthyRuntimeMarker(env._HERMES_GATEWAY)) {
|
|
3504
|
-
return "hermes";
|
|
3505
|
-
}
|
|
3506
|
-
if ((env.OPENCLAW_SERVICE_KIND ?? "").trim().toLowerCase() === "gateway") {
|
|
3507
|
-
return "openclaw";
|
|
3508
|
-
}
|
|
3509
|
-
if (!isTruthyRuntimeMarker(env.OKX_A2A_IN_GATEWAY)) {
|
|
3510
|
-
return null;
|
|
3511
|
-
}
|
|
3512
|
-
const provider = env.OKX_A2A_GATEWAY_PROVIDER;
|
|
3513
|
-
if (!provider) {
|
|
3514
|
-
return null;
|
|
3515
|
-
}
|
|
3516
|
-
try {
|
|
3517
|
-
const normalized = normalizeAiProvider(provider);
|
|
3518
|
-
return normalized === "openclaw" || normalized === "hermes" ? normalized : null;
|
|
3519
|
-
} catch {
|
|
3520
|
-
return null;
|
|
3521
|
-
}
|
|
3536
|
+
function codexCommandResolvesWithoutOverride(env, storedCommand, platform) {
|
|
3537
|
+
const envWithoutOverride = { ...env };
|
|
3538
|
+
delete envWithoutOverride[CODEX_COMMAND_OVERRIDE_ENV2];
|
|
3539
|
+
return resolveCodexCommandSource(envWithoutOverride, storedCommand, platform) !== null;
|
|
3522
3540
|
}
|
|
3523
|
-
function
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
ok: true,
|
|
3533
|
-
runtime: "unknown",
|
|
3534
|
-
provider: stored,
|
|
3535
|
-
previousProvider: stored,
|
|
3536
|
-
changed: false,
|
|
3537
|
-
action: "kept_existing",
|
|
3538
|
-
userMessage: `No AI runtime detected for the current environment; keeping the existing default provider (${formatAiProviderDisplayName(stored)}).`
|
|
3539
|
-
};
|
|
3540
|
-
}
|
|
3541
|
-
return {
|
|
3542
|
-
ok: false,
|
|
3543
|
-
runtime: "unknown",
|
|
3544
|
-
reason: "unknown_runtime",
|
|
3545
|
-
state: "blocked",
|
|
3546
|
-
message: "Could not determine the current AI provider.",
|
|
3547
|
-
userMessage: "Could not detect the current AI runtime and no default AI provider is configured. Run this from your AI environment (OpenClaw / Codex / Claude / Hermes), or set a provider manually: `okx-a2a ai-provider set --provider <codex|claude|hermes|openclaw>`."
|
|
3541
|
+
function probeCodexAuthStatus(command, env, platform, timeoutMs) {
|
|
3542
|
+
return new Promise((resolvePromise) => {
|
|
3543
|
+
let settled = false;
|
|
3544
|
+
const settle = (result) => {
|
|
3545
|
+
if (settled) {
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
3548
|
+
settled = true;
|
|
3549
|
+
resolvePromise(result);
|
|
3548
3550
|
};
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3551
|
+
let child;
|
|
3552
|
+
try {
|
|
3553
|
+
child = spawnCompat(command, ["login", "status"], {
|
|
3554
|
+
env,
|
|
3555
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3556
|
+
windowsHide: true
|
|
3557
|
+
});
|
|
3558
|
+
} catch (error) {
|
|
3559
|
+
settle({ ok: false, state: spawnErrorReadinessState(error) });
|
|
3560
|
+
return;
|
|
3561
|
+
}
|
|
3562
|
+
child.stdout?.resume();
|
|
3563
|
+
child.stderr?.resume();
|
|
3564
|
+
const timer = setTimeout(() => {
|
|
3565
|
+
killProcessTree(child, "SIGTERM", platform);
|
|
3566
|
+
setTimeout(() => {
|
|
3567
|
+
if (!child.killed || child.exitCode === null) {
|
|
3568
|
+
killProcessTree(child, "SIGKILL", platform);
|
|
3569
|
+
}
|
|
3570
|
+
}, CODEX_PROBE_KILL_GRACE_MS).unref();
|
|
3571
|
+
settle({ ok: false, state: "probe_timeout" });
|
|
3572
|
+
}, timeoutMs);
|
|
3573
|
+
timer.unref();
|
|
3574
|
+
child.once("error", (error) => {
|
|
3575
|
+
clearTimeout(timer);
|
|
3576
|
+
settle({ ok: false, state: spawnErrorReadinessState(error) });
|
|
3577
|
+
});
|
|
3578
|
+
child.once("close", (code) => {
|
|
3579
|
+
clearTimeout(timer);
|
|
3580
|
+
settle(code === 0 ? { ok: true } : { ok: false, state: "not_authenticated" });
|
|
3581
|
+
});
|
|
3582
|
+
});
|
|
3562
3583
|
}
|
|
3563
|
-
function
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
return
|
|
3584
|
+
function readCachedCodexReadiness(options = {}) {
|
|
3585
|
+
const env = options.env ?? process.env;
|
|
3586
|
+
const nowMs = options.nowMs ?? Date.now;
|
|
3587
|
+
const cached = readCacheEntry(options.cacheKey ?? codexReadinessCacheKey(env), nowMs());
|
|
3588
|
+
return cached ? { ...cached, fromCache: true } : null;
|
|
3568
3589
|
}
|
|
3569
|
-
function
|
|
3570
|
-
|
|
3590
|
+
function clearCodexReadinessCache() {
|
|
3591
|
+
readinessCache.clear();
|
|
3592
|
+
inFlightReadiness.clear();
|
|
3571
3593
|
}
|
|
3572
|
-
function
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3594
|
+
function checkCodexReadiness(options = {}) {
|
|
3595
|
+
const env = options.env ?? process.env;
|
|
3596
|
+
const nowMs = options.nowMs ?? Date.now;
|
|
3597
|
+
const cacheKey2 = options.cacheKey ?? codexReadinessCacheKey(env, options.persistOverrideCommand === true);
|
|
3598
|
+
if (!options.forceRefresh) {
|
|
3599
|
+
const cached = readCacheEntry(cacheKey2, nowMs());
|
|
3600
|
+
if (cached) {
|
|
3601
|
+
return Promise.resolve({ ...cached, fromCache: true });
|
|
3602
|
+
}
|
|
3603
|
+
const inFlight = inFlightReadiness.get(cacheKey2);
|
|
3604
|
+
if (inFlight) {
|
|
3605
|
+
return inFlight;
|
|
3581
3606
|
}
|
|
3582
|
-
|
|
3583
|
-
|
|
3607
|
+
}
|
|
3608
|
+
const pending = resolveCodexReadiness(options, env, cacheKey2, nowMs).finally(() => {
|
|
3609
|
+
if (inFlightReadiness.get(cacheKey2) === pending) {
|
|
3610
|
+
inFlightReadiness.delete(cacheKey2);
|
|
3584
3611
|
}
|
|
3585
|
-
|
|
3586
|
-
|
|
3612
|
+
});
|
|
3613
|
+
inFlightReadiness.set(cacheKey2, pending);
|
|
3614
|
+
return pending;
|
|
3615
|
+
}
|
|
3616
|
+
async function resolveCodexReadiness(options, env, cacheKey2, nowMs) {
|
|
3617
|
+
try {
|
|
3618
|
+
const platform = options.platform ?? process.platform;
|
|
3619
|
+
const store = options.store ?? null;
|
|
3620
|
+
const probeAuth = options.probeAuth ?? probeCodexAuthStatus;
|
|
3621
|
+
const storedCommand = store ? store.getAiProviderCommand("codex") : null;
|
|
3622
|
+
const candidate = resolveCodexCommandSource(env, storedCommand, platform);
|
|
3623
|
+
if (!candidate) {
|
|
3624
|
+
return storeCacheEntry(cacheKey2, nowMs, buildCodexReadinessResult("not_installed", null, null, false));
|
|
3625
|
+
}
|
|
3626
|
+
const probe = await probeAuth(candidate.command, env, platform, CODEX_AUTH_PROBE_TIMEOUT_MS);
|
|
3627
|
+
if (!probe.ok) {
|
|
3628
|
+
return storeCacheEntry(
|
|
3629
|
+
cacheKey2,
|
|
3630
|
+
nowMs,
|
|
3631
|
+
buildCodexReadinessResult(probe.state, candidate.command, candidate.source, false)
|
|
3632
|
+
);
|
|
3587
3633
|
}
|
|
3588
|
-
|
|
3634
|
+
const commandPersisted = persistReadyCodexCommand(
|
|
3635
|
+
candidate,
|
|
3636
|
+
store,
|
|
3637
|
+
options.persist,
|
|
3638
|
+
options.persistOverrideCommand === true
|
|
3639
|
+
);
|
|
3640
|
+
return storeCacheEntry(
|
|
3641
|
+
cacheKey2,
|
|
3642
|
+
nowMs,
|
|
3643
|
+
buildCodexReadinessResult("ready", candidate.command, candidate.source, commandPersisted)
|
|
3644
|
+
);
|
|
3645
|
+
} catch {
|
|
3646
|
+
return storeCacheEntry(cacheKey2, nowMs, buildCodexReadinessResult("probe_failed", null, null, false));
|
|
3589
3647
|
}
|
|
3590
|
-
return false;
|
|
3591
|
-
}
|
|
3592
|
-
function readParentProcessCommand(pid) {
|
|
3593
|
-
return readParentProcessCommandForPlatform(pid, process.platform);
|
|
3594
3648
|
}
|
|
3595
|
-
function
|
|
3596
|
-
if (
|
|
3597
|
-
return
|
|
3649
|
+
function persistReadyCodexCommand(candidate, store, persist, persistOverrideCommand) {
|
|
3650
|
+
if (persist === false || !store) {
|
|
3651
|
+
return false;
|
|
3598
3652
|
}
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
3602
|
-
});
|
|
3603
|
-
const parentResult = (0, import_node_child_process5.spawnSync)("ps", ["-p", String(pid), "-o", "ppid="], {
|
|
3604
|
-
encoding: "utf8",
|
|
3605
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
3606
|
-
});
|
|
3607
|
-
const command = typeof commandResult.stdout === "string" ? commandResult.stdout.trim() : "";
|
|
3608
|
-
const parentPidRaw = typeof parentResult.stdout === "string" ? parentResult.stdout.trim() : "";
|
|
3609
|
-
const parentPid = Number(parentPidRaw);
|
|
3610
|
-
if (commandResult.status !== 0 || parentResult.status !== 0 || !command || !Number.isFinite(parentPid)) {
|
|
3611
|
-
return null;
|
|
3653
|
+
if (candidate.source === "explicit_override" && !persistOverrideCommand) {
|
|
3654
|
+
return false;
|
|
3612
3655
|
}
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
return
|
|
3656
|
+
try {
|
|
3657
|
+
store.setAiProviderCommand("codex", candidate.command);
|
|
3658
|
+
return true;
|
|
3659
|
+
} catch {
|
|
3660
|
+
return false;
|
|
3618
3661
|
}
|
|
3662
|
+
}
|
|
3663
|
+
function buildCodexReadinessResult(state, command, commandSource, commandPersisted) {
|
|
3664
|
+
const { recoveryAction, recoveryGuidance } = buildCodexRecoveryGuidance(state, commandSource);
|
|
3665
|
+
const ready = state === "ready";
|
|
3619
3666
|
return {
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3667
|
+
provider: "codex",
|
|
3668
|
+
ready,
|
|
3669
|
+
state,
|
|
3670
|
+
authenticated: ready,
|
|
3671
|
+
command,
|
|
3672
|
+
commandSource,
|
|
3673
|
+
commandPersisted,
|
|
3674
|
+
recoveryAction,
|
|
3675
|
+
recoveryGuidance,
|
|
3676
|
+
fromCache: false
|
|
3627
3677
|
};
|
|
3628
3678
|
}
|
|
3629
|
-
function
|
|
3630
|
-
const
|
|
3631
|
-
if (!
|
|
3632
|
-
return null;
|
|
3633
|
-
}
|
|
3634
|
-
let parsed;
|
|
3635
|
-
try {
|
|
3636
|
-
parsed = JSON.parse(trimmed);
|
|
3637
|
-
} catch {
|
|
3638
|
-
return null;
|
|
3639
|
-
}
|
|
3640
|
-
const record = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
3641
|
-
if (!record || typeof record !== "object") {
|
|
3679
|
+
function readCacheEntry(cacheKey2, atMs) {
|
|
3680
|
+
const entry = readinessCache.get(cacheKey2);
|
|
3681
|
+
if (!entry) {
|
|
3642
3682
|
return null;
|
|
3643
3683
|
}
|
|
3644
|
-
|
|
3645
|
-
|
|
3684
|
+
if (atMs >= entry.expiresAtMs) {
|
|
3685
|
+
readinessCache.delete(cacheKey2);
|
|
3646
3686
|
return null;
|
|
3647
3687
|
}
|
|
3648
|
-
|
|
3649
|
-
const parentPid = Number.isInteger(rawParentPid) && rawParentPid > 0 ? rawParentPid : null;
|
|
3650
|
-
return { command: name.trim(), parentPid };
|
|
3688
|
+
return entry.result;
|
|
3651
3689
|
}
|
|
3652
|
-
function
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
logWinCompat(`${WIN_COMPAT_LOG_PREFIX} parent-process probe rejected non-integer pid=${String(pid)}`);
|
|
3656
|
-
return null;
|
|
3657
|
-
}
|
|
3658
|
-
const result = (0, import_node_child_process5.spawnSync)(probe.command, probe.args, {
|
|
3659
|
-
encoding: "utf8",
|
|
3660
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
3661
|
-
timeout: 5e3,
|
|
3662
|
-
windowsHide: true
|
|
3663
|
-
});
|
|
3664
|
-
if (result.error) {
|
|
3665
|
-
const spawnError = result.error;
|
|
3666
|
-
logWinCompat(
|
|
3667
|
-
`${WIN_COMPAT_LOG_PREFIX} parent-process probe failed pid=${pid} command=${probe.command} errorCode=${spawnError.code ?? "unknown"} error=${spawnError.message}`
|
|
3668
|
-
);
|
|
3669
|
-
return null;
|
|
3670
|
-
}
|
|
3671
|
-
if (result.status !== 0) {
|
|
3672
|
-
logWinCompat(
|
|
3673
|
-
`${WIN_COMPAT_LOG_PREFIX} parent-process probe exited pid=${pid} command=${probe.command} status=${String(result.status)} signal=${String(result.signal)}`
|
|
3674
|
-
);
|
|
3675
|
-
return null;
|
|
3676
|
-
}
|
|
3677
|
-
const stdout = typeof result.stdout === "string" ? result.stdout : "";
|
|
3678
|
-
const parsed = parseWindowsParentProcessJson(stdout);
|
|
3679
|
-
if (!parsed) {
|
|
3680
|
-
logWinCompat(
|
|
3681
|
-
`${WIN_COMPAT_LOG_PREFIX} parent-process probe unparsable output pid=${pid} stdout=${JSON.stringify(stdout.slice(0, 200))}`
|
|
3682
|
-
);
|
|
3683
|
-
return null;
|
|
3684
|
-
}
|
|
3685
|
-
if (process.env.OKX_A2A_WIN_COMPAT_VERBOSE === "1") {
|
|
3686
|
-
logWinCompat(
|
|
3687
|
-
`${WIN_COMPAT_LOG_PREFIX} parent-process probe pid=${pid} -> command=${parsed.command} parentPid=${parsed.parentPid ?? "null"}`
|
|
3688
|
-
);
|
|
3689
|
-
}
|
|
3690
|
-
return parsed;
|
|
3690
|
+
function storeCacheEntry(cacheKey2, nowMs, result) {
|
|
3691
|
+
readinessCache.set(cacheKey2, { result, expiresAtMs: nowMs() + CODEX_READINESS_CACHE_TTL_MS });
|
|
3692
|
+
return result;
|
|
3691
3693
|
}
|
|
3692
|
-
|
|
3693
|
-
const
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3694
|
+
function spawnErrorReadinessState(error) {
|
|
3695
|
+
const code = error?.code;
|
|
3696
|
+
return code === "ENOENT" ? "not_installed" : "probe_failed";
|
|
3697
|
+
}
|
|
3698
|
+
var CODEX_AUTH_PROBE_TIMEOUT_MS, CODEX_READINESS_CACHE_TTL_MS, CODEX_PROBE_KILL_GRACE_MS, CODEX_COMMAND_OVERRIDE_ENV2, CODEX_RECOVERY_BY_STATE, readinessCache, inFlightReadiness, CODEX_APP_BUNDLED_LOGIN_GUIDANCE;
|
|
3699
|
+
var init_codex_readiness = __esm({
|
|
3700
|
+
"src/codex-readiness.ts"() {
|
|
3701
|
+
"use strict";
|
|
3702
|
+
init_ai_command();
|
|
3703
|
+
init_win_spawn();
|
|
3704
|
+
CODEX_AUTH_PROBE_TIMEOUT_MS = 1e4;
|
|
3705
|
+
CODEX_READINESS_CACHE_TTL_MS = 6e4;
|
|
3706
|
+
CODEX_PROBE_KILL_GRACE_MS = 5e3;
|
|
3707
|
+
CODEX_COMMAND_OVERRIDE_ENV2 = "OKX_A2A_AI_CODEX_COMMAND";
|
|
3708
|
+
CODEX_RECOVERY_BY_STATE = {
|
|
3709
|
+
ready: { recoveryAction: "none", recoveryGuidance: "" },
|
|
3710
|
+
not_installed: {
|
|
3711
|
+
recoveryAction: "install_cli",
|
|
3712
|
+
recoveryGuidance: "Codex was not found. Install the Codex CLI or the Codex desktop app, then run `okx-a2a setup`."
|
|
3713
|
+
},
|
|
3714
|
+
not_authenticated: {
|
|
3715
|
+
recoveryAction: "run_login",
|
|
3716
|
+
recoveryGuidance: "Codex is installed but not logged in. Run `codex login`, then retry."
|
|
3717
|
+
},
|
|
3718
|
+
probe_timeout: {
|
|
3719
|
+
recoveryAction: "run_setup",
|
|
3720
|
+
recoveryGuidance: `Codex did not report its login status within ${CODEX_AUTH_PROBE_TIMEOUT_MS}ms. Run \`okx-a2a setup\` to re-verify the Codex CLI.`
|
|
3721
|
+
},
|
|
3722
|
+
probe_failed: {
|
|
3723
|
+
recoveryAction: "run_setup",
|
|
3724
|
+
recoveryGuidance: "Codex readiness could not be verified. Run `okx-a2a setup` to re-verify the Codex CLI."
|
|
3725
|
+
}
|
|
3724
3726
|
};
|
|
3727
|
+
readinessCache = /* @__PURE__ */ new Map();
|
|
3728
|
+
inFlightReadiness = /* @__PURE__ */ new Map();
|
|
3729
|
+
CODEX_APP_BUNDLED_LOGIN_GUIDANCE = "Codex was found in the Codex app bundle but is not logged in. Sign in from the Codex app, or run `okx-a2a setup --json` to re-verify Codex. `codex login` will not work: the bundled executable is not on PATH.";
|
|
3725
3730
|
}
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3731
|
+
});
|
|
3732
|
+
|
|
3733
|
+
// ../../node_modules/@sentry/utils/cjs/is.js
|
|
3734
|
+
var require_is = __commonJS({
|
|
3735
|
+
"../../node_modules/@sentry/utils/cjs/is.js"(exports2) {
|
|
3736
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3737
|
+
var objectToString = Object.prototype.toString;
|
|
3738
|
+
function isError(wat) {
|
|
3739
|
+
switch (objectToString.call(wat)) {
|
|
3740
|
+
case "[object Error]":
|
|
3741
|
+
case "[object Exception]":
|
|
3742
|
+
case "[object DOMException]":
|
|
3743
|
+
return true;
|
|
3744
|
+
default:
|
|
3745
|
+
return isInstanceOf(wat, Error);
|
|
3746
|
+
}
|
|
3730
3747
|
}
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
provider,
|
|
3734
|
-
source: "env",
|
|
3735
|
-
message: isInstalled(provider, detection) ? `default AI provider set to ${provider} from OKX_AGENT_TASK_AI_CLI` : `default AI provider set to ${provider} from OKX_AGENT_TASK_AI_CLI; availability was not verified on PATH`
|
|
3736
|
-
};
|
|
3737
|
-
}
|
|
3738
|
-
if (env.OKX_A2A_AI_PROVIDER) {
|
|
3739
|
-
const provider = normalizeAiProvider(env.OKX_A2A_AI_PROVIDER);
|
|
3740
|
-
if (!options.allowUnavailableProvider) {
|
|
3741
|
-
assertInstalled(provider, detection);
|
|
3748
|
+
function isBuiltin(wat, className) {
|
|
3749
|
+
return objectToString.call(wat) === `[object ${className}]`;
|
|
3742
3750
|
}
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
source: "env",
|
|
3746
|
-
message: isInstalled(provider, detection) ? `AI provider resolved from OKX_A2A_AI_PROVIDER: ${provider}` : `AI provider resolved from OKX_A2A_AI_PROVIDER: ${provider}; availability was not verified on PATH`
|
|
3747
|
-
};
|
|
3748
|
-
}
|
|
3749
|
-
const stored = options.store.getDefaultAiProvider();
|
|
3750
|
-
if (stored && (options.allowUnavailableProvider || isInstalled(stored, detection))) {
|
|
3751
|
-
return {
|
|
3752
|
-
provider: stored,
|
|
3753
|
-
source: "stored",
|
|
3754
|
-
message: isInstalled(stored, detection) ? `default AI provider is ${stored}` : `default AI provider is ${stored}; availability was not verified on PATH`
|
|
3755
|
-
};
|
|
3756
|
-
}
|
|
3757
|
-
if (stored) {
|
|
3758
|
-
options.stderr?.write(
|
|
3759
|
-
`[okx-agent-task] stored AI provider "${stored}" is not installed or not on PATH; selecting again.
|
|
3760
|
-
`
|
|
3761
|
-
);
|
|
3762
|
-
}
|
|
3763
|
-
if (options.promptIfMissing) {
|
|
3764
|
-
const provider = await promptForAiProvider({
|
|
3765
|
-
detection,
|
|
3766
|
-
stdin: options.stdin,
|
|
3767
|
-
stdout: options.stdout,
|
|
3768
|
-
currentProvider: stored ?? null
|
|
3769
|
-
});
|
|
3770
|
-
options.store.setDefaultAiProvider(provider);
|
|
3771
|
-
return {
|
|
3772
|
-
provider,
|
|
3773
|
-
source: "detected",
|
|
3774
|
-
message: `default AI provider set to ${provider}`
|
|
3775
|
-
};
|
|
3776
|
-
}
|
|
3777
|
-
const current = detectCurrentAiProvider(env);
|
|
3778
|
-
if (current && (options.allowUnavailableProvider || isInstalled(current, detection))) {
|
|
3779
|
-
return {
|
|
3780
|
-
provider: current,
|
|
3781
|
-
source: "env",
|
|
3782
|
-
message: isInstalled(current, detection) ? `AI provider resolved from current environment: ${current}` : `AI provider resolved from current environment: ${current}; availability was not verified on PATH`
|
|
3783
|
-
};
|
|
3784
|
-
}
|
|
3785
|
-
if (detection.available.length === 0) {
|
|
3786
|
-
throw new Error(
|
|
3787
|
-
`No supported AI CLI found. Install one of: ${AI_PROVIDERS.join(", ")}.`
|
|
3788
|
-
);
|
|
3789
|
-
}
|
|
3790
|
-
if (detection.available.length === 1) {
|
|
3791
|
-
const provider = detection.available[0];
|
|
3792
|
-
options.store.setDefaultAiProvider(provider);
|
|
3793
|
-
return {
|
|
3794
|
-
provider,
|
|
3795
|
-
source: "detected",
|
|
3796
|
-
message: `detected only ${provider}; default AI provider set to ${provider}`
|
|
3797
|
-
};
|
|
3798
|
-
}
|
|
3799
|
-
throw new Error(
|
|
3800
|
-
`Multiple AI CLIs are installed (${detection.available.join(", ")}) but no current AI environment was detected. Start the daemon from an AI session, pass --ai-provider <provider>, or set OKX_A2A_AI_PROVIDER.`
|
|
3801
|
-
);
|
|
3802
|
-
}
|
|
3803
|
-
function resolveConfiguredAiProvider(options) {
|
|
3804
|
-
const env = options.env ?? process.env;
|
|
3805
|
-
const explicit = env.OKX_AGENT_TASK_AI_CLI ?? env.OKX_A2A_AI_PROVIDER;
|
|
3806
|
-
if (explicit) {
|
|
3807
|
-
return normalizeAiProvider(explicit);
|
|
3808
|
-
}
|
|
3809
|
-
return options.store.getDefaultAiProvider();
|
|
3810
|
-
}
|
|
3811
|
-
function resolveConfiguredAiProviderForJob(options) {
|
|
3812
|
-
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
3813
|
-
if (jobId) {
|
|
3814
|
-
const binding = options.store.getJobProviderBinding(jobId);
|
|
3815
|
-
if (binding) {
|
|
3816
|
-
bindHermesJobRouteFromEnvIfAvailable(options.store, {
|
|
3817
|
-
jobId,
|
|
3818
|
-
provider: binding.provider,
|
|
3819
|
-
env: options.env
|
|
3820
|
-
});
|
|
3821
|
-
return binding.provider;
|
|
3822
|
-
}
|
|
3823
|
-
}
|
|
3824
|
-
const provider = resolveConfiguredAiProvider({
|
|
3825
|
-
store: options.store,
|
|
3826
|
-
env: options.env
|
|
3827
|
-
});
|
|
3828
|
-
if (jobId && provider) {
|
|
3829
|
-
return bindJobProviderWithRouteIfAvailable(options.store, {
|
|
3830
|
-
jobId,
|
|
3831
|
-
provider,
|
|
3832
|
-
env: options.env
|
|
3833
|
-
}).binding.provider;
|
|
3834
|
-
}
|
|
3835
|
-
return provider;
|
|
3836
|
-
}
|
|
3837
|
-
function bindJobProviderToCurrentDefaultIfMissing(options) {
|
|
3838
|
-
const store = options.store ?? null;
|
|
3839
|
-
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
3840
|
-
if (!store || !jobId) {
|
|
3841
|
-
return null;
|
|
3842
|
-
}
|
|
3843
|
-
const existing = store.getJobProviderBinding(jobId);
|
|
3844
|
-
if (existing) {
|
|
3845
|
-
bindHermesJobRouteFromEnvIfAvailable(store, {
|
|
3846
|
-
jobId,
|
|
3847
|
-
provider: existing.provider,
|
|
3848
|
-
env: options.env
|
|
3849
|
-
});
|
|
3850
|
-
return {
|
|
3851
|
-
provider: existing.provider,
|
|
3852
|
-
created: false
|
|
3853
|
-
};
|
|
3854
|
-
}
|
|
3855
|
-
const provider = store.getDefaultAiProvider();
|
|
3856
|
-
if (!provider) {
|
|
3857
|
-
return null;
|
|
3858
|
-
}
|
|
3859
|
-
const result = bindJobProviderWithRouteIfAvailable(store, {
|
|
3860
|
-
jobId,
|
|
3861
|
-
provider,
|
|
3862
|
-
env: options.env
|
|
3863
|
-
});
|
|
3864
|
-
return {
|
|
3865
|
-
provider: result.binding.provider,
|
|
3866
|
-
created: result.created
|
|
3867
|
-
};
|
|
3868
|
-
}
|
|
3869
|
-
function bindJobProviderToCurrentRuntimeIfMissing(options) {
|
|
3870
|
-
const store = options.store ?? null;
|
|
3871
|
-
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
3872
|
-
if (!store || !jobId) {
|
|
3873
|
-
return {
|
|
3874
|
-
ok: false,
|
|
3875
|
-
jobId: jobId ?? "",
|
|
3876
|
-
provider: null,
|
|
3877
|
-
currentProvider: null,
|
|
3878
|
-
created: false,
|
|
3879
|
-
binding: null,
|
|
3880
|
-
reason: "missing_job_id"
|
|
3881
|
-
};
|
|
3882
|
-
}
|
|
3883
|
-
const existing = store.getJobProviderBinding(jobId);
|
|
3884
|
-
if (existing) {
|
|
3885
|
-
const binding = bindHermesJobRouteFromEnvIfAvailable(store, {
|
|
3886
|
-
jobId,
|
|
3887
|
-
provider: existing.provider,
|
|
3888
|
-
env: options.env
|
|
3889
|
-
}) ?? existing;
|
|
3890
|
-
return {
|
|
3891
|
-
ok: true,
|
|
3892
|
-
jobId,
|
|
3893
|
-
provider: binding.provider,
|
|
3894
|
-
currentProvider: null,
|
|
3895
|
-
created: false,
|
|
3896
|
-
binding,
|
|
3897
|
-
reason: "already_bound"
|
|
3898
|
-
};
|
|
3899
|
-
}
|
|
3900
|
-
const switched = switchProvider({ store, env: options.env ?? process.env });
|
|
3901
|
-
if (!switched.ok || !switched.provider) {
|
|
3902
|
-
return {
|
|
3903
|
-
ok: false,
|
|
3904
|
-
jobId,
|
|
3905
|
-
provider: null,
|
|
3906
|
-
currentProvider: null,
|
|
3907
|
-
created: false,
|
|
3908
|
-
binding: null,
|
|
3909
|
-
reason: "unknown_runtime"
|
|
3910
|
-
};
|
|
3911
|
-
}
|
|
3912
|
-
const currentProvider = switched.provider;
|
|
3913
|
-
const result = bindJobProviderWithRouteIfAvailable(store, {
|
|
3914
|
-
jobId,
|
|
3915
|
-
provider: currentProvider,
|
|
3916
|
-
env: options.env
|
|
3917
|
-
});
|
|
3918
|
-
return {
|
|
3919
|
-
ok: true,
|
|
3920
|
-
jobId,
|
|
3921
|
-
provider: result.binding.provider,
|
|
3922
|
-
currentProvider,
|
|
3923
|
-
created: result.created,
|
|
3924
|
-
binding: result.binding,
|
|
3925
|
-
reason: result.created ? "created" : "already_bound"
|
|
3926
|
-
};
|
|
3927
|
-
}
|
|
3928
|
-
function resolveAiProviderForDispatch(options) {
|
|
3929
|
-
const env = options.env ?? process.env;
|
|
3930
|
-
const commandExists2 = options.commandExists ?? commandExists;
|
|
3931
|
-
const detection = withStoredCodexAvailability(
|
|
3932
|
-
detectAiProviders(commandExists2, env),
|
|
3933
|
-
options.store,
|
|
3934
|
-
commandExists2
|
|
3935
|
-
);
|
|
3936
|
-
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
3937
|
-
if (jobId) {
|
|
3938
|
-
const binding = options.store.getJobProviderBinding(jobId);
|
|
3939
|
-
if (binding) {
|
|
3940
|
-
assertInstalled(binding.provider, detection);
|
|
3941
|
-
return binding.provider;
|
|
3942
|
-
}
|
|
3943
|
-
}
|
|
3944
|
-
if (env.OKX_AGENT_TASK_AI_CLI) {
|
|
3945
|
-
const provider = normalizeAiProvider(env.OKX_AGENT_TASK_AI_CLI);
|
|
3946
|
-
assertInstalled(provider, detection);
|
|
3947
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, provider);
|
|
3948
|
-
}
|
|
3949
|
-
if (env.OKX_A2A_AI_PROVIDER) {
|
|
3950
|
-
const provider = normalizeAiProvider(env.OKX_A2A_AI_PROVIDER);
|
|
3951
|
-
assertInstalled(provider, detection);
|
|
3952
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, provider);
|
|
3953
|
-
}
|
|
3954
|
-
if (jobId) {
|
|
3955
|
-
const stored2 = options.store.getDefaultAiProvider();
|
|
3956
|
-
if (stored2) {
|
|
3957
|
-
assertInstalled(stored2, detection);
|
|
3958
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, stored2);
|
|
3959
|
-
}
|
|
3960
|
-
}
|
|
3961
|
-
const current = detectCurrentAiProvider(env);
|
|
3962
|
-
if (current && isInstalled(current, detection)) {
|
|
3963
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, current);
|
|
3964
|
-
}
|
|
3965
|
-
if (options.sessionKey) {
|
|
3966
|
-
const existing = options.store.listAiSessions(options.sessionKey).map((session) => session.provider).filter((provider) => isInstalled(provider, detection));
|
|
3967
|
-
const unique = [...new Set(existing)];
|
|
3968
|
-
if (unique.length === 1) {
|
|
3969
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, unique[0]);
|
|
3970
|
-
}
|
|
3971
|
-
}
|
|
3972
|
-
const stored = options.store.getDefaultAiProvider();
|
|
3973
|
-
if (stored) {
|
|
3974
|
-
assertInstalled(stored, detection);
|
|
3975
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, stored);
|
|
3976
|
-
}
|
|
3977
|
-
if (detection.available.length === 1) {
|
|
3978
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, detection.available[0]);
|
|
3979
|
-
}
|
|
3980
|
-
if (detection.available.length > 1) {
|
|
3981
|
-
throw new Error(
|
|
3982
|
-
`Multiple AI CLIs are installed (${detection.available.join(", ")}) but no current AI environment was detected. Set OKX_A2A_AI_PROVIDER or pass --provider/--ai-provider for this run.`
|
|
3983
|
-
);
|
|
3984
|
-
}
|
|
3985
|
-
throw new Error(`No supported AI CLI found. Install one of: ${AI_PROVIDERS.join(", ")}.`);
|
|
3986
|
-
}
|
|
3987
|
-
function bindDispatchProviderIfNeeded(store, jobId, env, provider) {
|
|
3988
|
-
if (!jobId) {
|
|
3989
|
-
return provider;
|
|
3990
|
-
}
|
|
3991
|
-
return bindJobProviderWithRouteIfAvailable(store, { jobId, provider, env }).binding.provider;
|
|
3992
|
-
}
|
|
3993
|
-
function withStoredCodexAvailability(detection, store, commandExists2) {
|
|
3994
|
-
if (detection.codex) {
|
|
3995
|
-
return detection;
|
|
3996
|
-
}
|
|
3997
|
-
const command = store.getAiProviderCommand("codex");
|
|
3998
|
-
if (!command || !commandExists2(command)) {
|
|
3999
|
-
return detection;
|
|
4000
|
-
}
|
|
4001
|
-
return {
|
|
4002
|
-
...detection,
|
|
4003
|
-
codex: true,
|
|
4004
|
-
available: detection.available.includes("codex") ? detection.available : ["codex", ...detection.available]
|
|
4005
|
-
};
|
|
4006
|
-
}
|
|
4007
|
-
function bindJobProviderWithRouteIfAvailable(store, input) {
|
|
4008
|
-
const result = store.bindJobProviderIfMissing({
|
|
4009
|
-
jobId: input.jobId,
|
|
4010
|
-
provider: input.provider
|
|
4011
|
-
});
|
|
4012
|
-
const routed = bindHermesJobRouteFromEnvIfAvailable(store, {
|
|
4013
|
-
jobId: input.jobId,
|
|
4014
|
-
provider: result.binding.provider,
|
|
4015
|
-
env: input.env
|
|
4016
|
-
});
|
|
4017
|
-
return routed ? { ...result, binding: routed } : result;
|
|
4018
|
-
}
|
|
4019
|
-
function bindHermesJobRouteFromEnvIfAvailable(store, input) {
|
|
4020
|
-
if (input.provider !== "hermes" || typeof store.upsertJobGatewayRoute !== "function") {
|
|
4021
|
-
return null;
|
|
4022
|
-
}
|
|
4023
|
-
const route = currentHermesGatewayRouteFromEnv(input.env ?? process.env);
|
|
4024
|
-
if (!route) {
|
|
4025
|
-
return null;
|
|
4026
|
-
}
|
|
4027
|
-
return store.upsertJobGatewayRoute({
|
|
4028
|
-
jobId: input.jobId,
|
|
4029
|
-
provider: "hermes",
|
|
4030
|
-
route
|
|
4031
|
-
});
|
|
4032
|
-
}
|
|
4033
|
-
function currentHermesGatewayRouteFromEnv(env) {
|
|
4034
|
-
const platform = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_PLATFORM);
|
|
4035
|
-
const chatId = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_CHAT_ID);
|
|
4036
|
-
const envGatewaySessionKey = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_SESSION_KEY);
|
|
4037
|
-
const parsedGatewaySession = parseHermesGatewaySessionKey(envGatewaySessionKey);
|
|
4038
|
-
if (platform && chatId) {
|
|
4039
|
-
const threadId = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_THREAD_ID) ?? matchingParsedThreadId(parsedGatewaySession, { platform, chatId });
|
|
4040
|
-
return buildHermesGatewayRoute({
|
|
4041
|
-
platform,
|
|
4042
|
-
chatId,
|
|
4043
|
-
threadId,
|
|
4044
|
-
sessionKey: envGatewaySessionKey
|
|
4045
|
-
});
|
|
4046
|
-
}
|
|
4047
|
-
const sessionRoute = routeFromHermesSessionKey(env.HERMES_SESSION_KEY);
|
|
4048
|
-
return sessionRoute ? buildHermesGatewayRoute(sessionRoute) : null;
|
|
4049
|
-
}
|
|
4050
|
-
function buildHermesGatewayRoute(input) {
|
|
4051
|
-
const threadId = normalizeOptionalText(input.threadId) ?? "";
|
|
4052
|
-
const sessionKey = normalizeOptionalText(input.sessionKey) ?? (threadId ? hermesThreadSessionKey(input.platform, input.chatId, threadId) : `agent:main:${input.platform}:dm:${encodeURIComponent(input.chatId)}`);
|
|
4053
|
-
return {
|
|
4054
|
-
platform: input.platform,
|
|
4055
|
-
chatId: input.chatId,
|
|
4056
|
-
chatName: "",
|
|
4057
|
-
chatType: threadId ? "thread" : "dm",
|
|
4058
|
-
threadId,
|
|
4059
|
-
userId: "",
|
|
4060
|
-
userName: "",
|
|
4061
|
-
sessionKey,
|
|
4062
|
-
gatewaySessionKey: sessionKey
|
|
4063
|
-
};
|
|
4064
|
-
}
|
|
4065
|
-
function hermesThreadSessionKey(platform, chatId, threadId) {
|
|
4066
|
-
const encodedChatId = encodeURIComponent(chatId);
|
|
4067
|
-
const encodedThreadId = encodeURIComponent(threadId);
|
|
4068
|
-
return platform === "telegram" ? `agent:main:${platform}:dm:${encodedChatId}:${encodedThreadId}` : `agent:main:${platform}:thread:${encodedChatId}:${encodedThreadId}`;
|
|
4069
|
-
}
|
|
4070
|
-
function routeFromHermesSessionKey(value) {
|
|
4071
|
-
const sessionKey = normalizeOptionalText(value);
|
|
4072
|
-
if (!sessionKey) {
|
|
4073
|
-
return null;
|
|
4074
|
-
}
|
|
4075
|
-
const parsed = parseHermesGatewaySessionKey(sessionKey);
|
|
4076
|
-
return parsed ? { ...parsed, sessionKey } : null;
|
|
4077
|
-
}
|
|
4078
|
-
function parseHermesGatewaySessionKey(sessionKey) {
|
|
4079
|
-
const normalized = normalizeOptionalText(sessionKey);
|
|
4080
|
-
if (!normalized) {
|
|
4081
|
-
return null;
|
|
4082
|
-
}
|
|
4083
|
-
const parts = normalized.startsWith("agent:") ? normalized.split(":").slice(2) : normalized.split(":");
|
|
4084
|
-
if (parts.length < 3) {
|
|
4085
|
-
return null;
|
|
4086
|
-
}
|
|
4087
|
-
const [platform, scope, ...rest] = parts;
|
|
4088
|
-
if (!platform || platform === "okx-a2a" || platform === "backup" || platform === "job") {
|
|
4089
|
-
return null;
|
|
4090
|
-
}
|
|
4091
|
-
if (scope === "dm" && rest[0]) {
|
|
4092
|
-
return {
|
|
4093
|
-
platform,
|
|
4094
|
-
chatId: safeDecodeURIComponent2(rest[0]),
|
|
4095
|
-
...platform === "telegram" && rest[1] ? { threadId: safeDecodeURIComponent2(rest.slice(1).join(":")) } : {}
|
|
4096
|
-
};
|
|
4097
|
-
}
|
|
4098
|
-
if (scope === "thread" && rest[0] && rest[1]) {
|
|
4099
|
-
return {
|
|
4100
|
-
platform,
|
|
4101
|
-
chatId: safeDecodeURIComponent2(rest[0]),
|
|
4102
|
-
threadId: safeDecodeURIComponent2(rest.slice(1).join(":"))
|
|
4103
|
-
};
|
|
4104
|
-
}
|
|
4105
|
-
return null;
|
|
4106
|
-
}
|
|
4107
|
-
function matchingParsedThreadId(parsed, expected) {
|
|
4108
|
-
if (!parsed?.threadId) {
|
|
4109
|
-
return null;
|
|
4110
|
-
}
|
|
4111
|
-
return parsed.platform === expected.platform && parsed.chatId === expected.chatId ? parsed.threadId : null;
|
|
4112
|
-
}
|
|
4113
|
-
function safeDecodeURIComponent2(value) {
|
|
4114
|
-
try {
|
|
4115
|
-
return decodeURIComponent(value);
|
|
4116
|
-
} catch {
|
|
4117
|
-
return value;
|
|
4118
|
-
}
|
|
4119
|
-
}
|
|
4120
|
-
function normalizeOptionalJobId2(value) {
|
|
4121
|
-
const normalized = value?.trim();
|
|
4122
|
-
return normalized ? normalized : null;
|
|
4123
|
-
}
|
|
4124
|
-
function normalizeOptionalText(value) {
|
|
4125
|
-
const normalized = value?.trim();
|
|
4126
|
-
return normalized ? normalized : null;
|
|
4127
|
-
}
|
|
4128
|
-
function isInstalled(provider, detection) {
|
|
4129
|
-
return detection.available.includes(provider);
|
|
4130
|
-
}
|
|
4131
|
-
function assertInstalled(provider, detection) {
|
|
4132
|
-
if (!isInstalled(provider, detection)) {
|
|
4133
|
-
throw new Error(`AI provider "${provider}" is not installed or not on PATH.`);
|
|
4134
|
-
}
|
|
4135
|
-
}
|
|
4136
|
-
function readProviderCommand(provider, env) {
|
|
4137
|
-
return env[`OKX_A2A_AI_${provider.toUpperCase()}_COMMAND`] ?? provider;
|
|
4138
|
-
}
|
|
4139
|
-
async function promptForAiProvider(options) {
|
|
4140
|
-
const stdin = options.stdin ?? process.stdin;
|
|
4141
|
-
const stdout = options.stdout ?? process.stdout;
|
|
4142
|
-
if (!stdin.isTTY || !stdout.isTTY) {
|
|
4143
|
-
throw new Error(
|
|
4144
|
-
`No AI provider is configured. Run \`okx-a2a daemon start --ai-provider <${AI_PROVIDERS.join("|")}>\` or set OKX_A2A_AI_PROVIDER.`
|
|
4145
|
-
);
|
|
4146
|
-
}
|
|
4147
|
-
stdout.write("AI provider is required before starting okx-a2a.\n");
|
|
4148
|
-
stdout.write("Tip: if the selected AI provider is unavailable, task processing cannot proceed.\n\n");
|
|
4149
|
-
if (options.currentProvider) {
|
|
4150
|
-
stdout.write(`Current AI provider: ${options.currentProvider}
|
|
4151
|
-
|
|
4152
|
-
`);
|
|
4153
|
-
}
|
|
4154
|
-
if (options.detection.available.length === 0) {
|
|
4155
|
-
stdout.write("Supported AI providers:\n");
|
|
4156
|
-
AI_PROVIDERS.forEach((provider, index2) => {
|
|
4157
|
-
const status = isInstalled(provider, options.detection) ? "installed" : "not found on PATH";
|
|
4158
|
-
stdout.write(` ${index2 + 1}. ${provider} (${status})
|
|
4159
|
-
`);
|
|
4160
|
-
});
|
|
4161
|
-
stdout.write("\n");
|
|
4162
|
-
throw new Error(`No supported AI CLI found. Install one of: ${AI_PROVIDERS.join(", ")}.`);
|
|
4163
|
-
}
|
|
4164
|
-
if (typeof stdin.setRawMode === "function") {
|
|
4165
|
-
return promptForAiProviderWithArrows({
|
|
4166
|
-
detection: options.detection,
|
|
4167
|
-
stdin,
|
|
4168
|
-
stdout,
|
|
4169
|
-
currentProvider: options.currentProvider
|
|
4170
|
-
});
|
|
4171
|
-
}
|
|
4172
|
-
stdout.write("Supported AI providers:\n");
|
|
4173
|
-
AI_PROVIDERS.forEach((provider, index2) => {
|
|
4174
|
-
const status = isInstalled(provider, options.detection) ? "installed" : "not found on PATH";
|
|
4175
|
-
stdout.write(` ${index2 + 1}. ${provider} (${status})
|
|
4176
|
-
`);
|
|
4177
|
-
});
|
|
4178
|
-
stdout.write("\n");
|
|
4179
|
-
const rl = (0, import_promises6.createInterface)({ input: stdin, output: stdout });
|
|
4180
|
-
try {
|
|
4181
|
-
while (true) {
|
|
4182
|
-
const answer = (await rl.question("Choose an installed AI provider by number or name: ")).trim();
|
|
4183
|
-
const provider = parseProviderChoice(answer);
|
|
4184
|
-
if (!provider) {
|
|
4185
|
-
stdout.write(`Invalid choice. Use one of: ${AI_PROVIDERS.join(", ")}.
|
|
4186
|
-
`);
|
|
4187
|
-
continue;
|
|
4188
|
-
}
|
|
4189
|
-
if (!isInstalled(provider, options.detection)) {
|
|
4190
|
-
stdout.write(`"${provider}" is not installed or not on PATH. Choose an installed provider.
|
|
4191
|
-
`);
|
|
4192
|
-
continue;
|
|
4193
|
-
}
|
|
4194
|
-
return provider;
|
|
4195
|
-
}
|
|
4196
|
-
} finally {
|
|
4197
|
-
rl.close();
|
|
4198
|
-
}
|
|
4199
|
-
}
|
|
4200
|
-
async function promptForAiProviderWithArrows(options) {
|
|
4201
|
-
let selectedIndex = AI_PROVIDERS.findIndex((provider) => provider === options.currentProvider && isInstalled(provider, options.detection));
|
|
4202
|
-
if (selectedIndex < 0) {
|
|
4203
|
-
selectedIndex = AI_PROVIDERS.findIndex((provider) => isInstalled(provider, options.detection));
|
|
4204
|
-
}
|
|
4205
|
-
if (selectedIndex < 0) {
|
|
4206
|
-
selectedIndex = 0;
|
|
4207
|
-
}
|
|
4208
|
-
let renderedLines = 0;
|
|
4209
|
-
const render = (message) => {
|
|
4210
|
-
if (renderedLines > 0) {
|
|
4211
|
-
(0, import_node_readline.moveCursor)(options.stdout, 0, -renderedLines);
|
|
4212
|
-
(0, import_node_readline.cursorTo)(options.stdout, 0);
|
|
4213
|
-
(0, import_node_readline.clearScreenDown)(options.stdout);
|
|
4214
|
-
}
|
|
4215
|
-
const lines = [
|
|
4216
|
-
"Use \u2191/\u2193 to move, Enter to confirm.",
|
|
4217
|
-
"",
|
|
4218
|
-
...AI_PROVIDERS.map((provider, index2) => {
|
|
4219
|
-
const selected = index2 === selectedIndex ? "\u276F" : " ";
|
|
4220
|
-
const status = isInstalled(provider, options.detection) ? "installed" : "not found on PATH";
|
|
4221
|
-
const current = provider === options.currentProvider ? ", current" : "";
|
|
4222
|
-
return `${selected} ${provider} (${status}${current})`;
|
|
4223
|
-
}),
|
|
4224
|
-
...message ? ["", message] : []
|
|
4225
|
-
];
|
|
4226
|
-
options.stdout.write(`${lines.join("\n")}
|
|
4227
|
-
`);
|
|
4228
|
-
renderedLines = lines.length;
|
|
4229
|
-
};
|
|
4230
|
-
(0, import_node_readline.emitKeypressEvents)(options.stdin);
|
|
4231
|
-
options.stdin.setRawMode(true);
|
|
4232
|
-
options.stdin.resume();
|
|
4233
|
-
options.stdout.write("\x1B[?25l");
|
|
4234
|
-
render();
|
|
4235
|
-
return await new Promise((resolve11, reject) => {
|
|
4236
|
-
const cleanup = () => {
|
|
4237
|
-
options.stdin.off("keypress", onKeypress);
|
|
4238
|
-
options.stdin.setRawMode(false);
|
|
4239
|
-
options.stdin.pause();
|
|
4240
|
-
options.stdout.write("\x1B[?25h");
|
|
4241
|
-
};
|
|
4242
|
-
const clearRendered = () => {
|
|
4243
|
-
if (renderedLines > 0) {
|
|
4244
|
-
(0, import_node_readline.moveCursor)(options.stdout, 0, -renderedLines);
|
|
4245
|
-
(0, import_node_readline.cursorTo)(options.stdout, 0);
|
|
4246
|
-
(0, import_node_readline.clearScreenDown)(options.stdout);
|
|
4247
|
-
}
|
|
4248
|
-
};
|
|
4249
|
-
const finish = (provider) => {
|
|
4250
|
-
cleanup();
|
|
4251
|
-
clearRendered();
|
|
4252
|
-
options.stdout.write(`Selected AI provider: ${provider}
|
|
4253
|
-
`);
|
|
4254
|
-
resolve11(provider);
|
|
4255
|
-
};
|
|
4256
|
-
const onKeypress = (_str, key) => {
|
|
4257
|
-
if (key.ctrl && key.name === "c") {
|
|
4258
|
-
cleanup();
|
|
4259
|
-
clearRendered();
|
|
4260
|
-
options.stdout.write(
|
|
4261
|
-
options.currentProvider ? `AI provider selection cancelled. Keeping current provider: ${options.currentProvider}
|
|
4262
|
-
` : "AI provider selection cancelled. No provider was configured.\n"
|
|
4263
|
-
);
|
|
4264
|
-
reject(new UserCancelledAiProviderSelectionError());
|
|
4265
|
-
return;
|
|
4266
|
-
}
|
|
4267
|
-
if (key.name === "up") {
|
|
4268
|
-
selectedIndex = (selectedIndex + AI_PROVIDERS.length - 1) % AI_PROVIDERS.length;
|
|
4269
|
-
render();
|
|
4270
|
-
return;
|
|
4271
|
-
}
|
|
4272
|
-
if (key.name === "down") {
|
|
4273
|
-
selectedIndex = (selectedIndex + 1) % AI_PROVIDERS.length;
|
|
4274
|
-
render();
|
|
4275
|
-
return;
|
|
4276
|
-
}
|
|
4277
|
-
if (key.name === "return" || key.name === "enter") {
|
|
4278
|
-
const provider = AI_PROVIDERS[selectedIndex];
|
|
4279
|
-
if (!isInstalled(provider, options.detection)) {
|
|
4280
|
-
render(`"${provider}" is not installed or not on PATH. Choose an installed provider.`);
|
|
4281
|
-
return;
|
|
4282
|
-
}
|
|
4283
|
-
finish(provider);
|
|
4284
|
-
}
|
|
4285
|
-
};
|
|
4286
|
-
options.stdin.on("keypress", onKeypress);
|
|
4287
|
-
});
|
|
4288
|
-
}
|
|
4289
|
-
function parseProviderChoice(answer) {
|
|
4290
|
-
if (!answer) {
|
|
4291
|
-
return null;
|
|
4292
|
-
}
|
|
4293
|
-
const number = Number(answer);
|
|
4294
|
-
if (Number.isInteger(number) && number >= 1 && number <= AI_PROVIDERS.length) {
|
|
4295
|
-
return AI_PROVIDERS[number - 1];
|
|
4296
|
-
}
|
|
4297
|
-
try {
|
|
4298
|
-
return normalizeAiProvider(answer);
|
|
4299
|
-
} catch {
|
|
4300
|
-
return null;
|
|
4301
|
-
}
|
|
4302
|
-
}
|
|
4303
|
-
var import_node_child_process5, import_promises6, import_node_readline, AI_PROVIDERS, UserCancelledAiProviderSelectionError;
|
|
4304
|
-
var init_ai_provider = __esm({
|
|
4305
|
-
"src/ai-provider.ts"() {
|
|
4306
|
-
"use strict";
|
|
4307
|
-
import_node_child_process5 = require("node:child_process");
|
|
4308
|
-
import_promises6 = require("node:readline/promises");
|
|
4309
|
-
import_node_readline = require("node:readline");
|
|
4310
|
-
init_ai_command();
|
|
4311
|
-
init_win_spawn();
|
|
4312
|
-
AI_PROVIDERS = ["codex", "claude", "hermes", "openclaw"];
|
|
4313
|
-
UserCancelledAiProviderSelectionError = class extends Error {
|
|
4314
|
-
constructor() {
|
|
4315
|
-
super("AI provider selection cancelled by user.");
|
|
4316
|
-
this.name = "UserCancelledAiProviderSelectionError";
|
|
4317
|
-
}
|
|
4318
|
-
};
|
|
4319
|
-
}
|
|
4320
|
-
});
|
|
4321
|
-
|
|
4322
|
-
// ../../node_modules/@sentry/utils/cjs/is.js
|
|
4323
|
-
var require_is = __commonJS({
|
|
4324
|
-
"../../node_modules/@sentry/utils/cjs/is.js"(exports2) {
|
|
4325
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
4326
|
-
var objectToString = Object.prototype.toString;
|
|
4327
|
-
function isError(wat) {
|
|
4328
|
-
switch (objectToString.call(wat)) {
|
|
4329
|
-
case "[object Error]":
|
|
4330
|
-
case "[object Exception]":
|
|
4331
|
-
case "[object DOMException]":
|
|
4332
|
-
return true;
|
|
4333
|
-
default:
|
|
4334
|
-
return isInstanceOf(wat, Error);
|
|
4335
|
-
}
|
|
4336
|
-
}
|
|
4337
|
-
function isBuiltin(wat, className) {
|
|
4338
|
-
return objectToString.call(wat) === `[object ${className}]`;
|
|
4339
|
-
}
|
|
4340
|
-
function isErrorEvent(wat) {
|
|
4341
|
-
return isBuiltin(wat, "ErrorEvent");
|
|
3751
|
+
function isErrorEvent(wat) {
|
|
3752
|
+
return isBuiltin(wat, "ErrorEvent");
|
|
4342
3753
|
}
|
|
4343
3754
|
function isDOMError(wat) {
|
|
4344
3755
|
return isBuiltin(wat, "DOMError");
|
|
@@ -6182,7 +5593,7 @@ var require_path = __commonJS({
|
|
|
6182
5593
|
return outputParts.join("/");
|
|
6183
5594
|
}
|
|
6184
5595
|
function normalizePath(path2) {
|
|
6185
|
-
const isPathAbsolute =
|
|
5596
|
+
const isPathAbsolute = isAbsolute4(path2);
|
|
6186
5597
|
const trailingSlash = path2.slice(-1) === "/";
|
|
6187
5598
|
let normalizedPath = normalizeArray(
|
|
6188
5599
|
path2.split("/").filter((p) => !!p),
|
|
@@ -6196,7 +5607,7 @@ var require_path = __commonJS({
|
|
|
6196
5607
|
}
|
|
6197
5608
|
return (isPathAbsolute ? "/" : "") + normalizedPath;
|
|
6198
5609
|
}
|
|
6199
|
-
function
|
|
5610
|
+
function isAbsolute4(path2) {
|
|
6200
5611
|
return path2.charAt(0) === "/";
|
|
6201
5612
|
}
|
|
6202
5613
|
function join31(...args) {
|
|
@@ -6223,7 +5634,7 @@ var require_path = __commonJS({
|
|
|
6223
5634
|
}
|
|
6224
5635
|
exports2.basename = basename8;
|
|
6225
5636
|
exports2.dirname = dirname9;
|
|
6226
|
-
exports2.isAbsolute =
|
|
5637
|
+
exports2.isAbsolute = isAbsolute4;
|
|
6227
5638
|
exports2.join = join31;
|
|
6228
5639
|
exports2.normalizePath = normalizePath;
|
|
6229
5640
|
exports2.relative = relative;
|
|
@@ -19627,6 +19038,14 @@ var init_events = __esm({
|
|
|
19627
19038
|
AI_RUN_STARTED: "AI run started",
|
|
19628
19039
|
AI_RUN_COMPLETED: "AI run completed",
|
|
19629
19040
|
AI_RUN_SKIPPED: "AI run skipped",
|
|
19041
|
+
// Codex GUI runtime detection / binding checkpoints. All four are logger.info,
|
|
19042
|
+
// so each one must also be listed in SENTRY_INFO_ALLOWLIST (index.ts): info()
|
|
19043
|
+
// returns early for an unlisted event, which ships a checkpoint that emits
|
|
19044
|
+
// nothing while every code path looks implemented.
|
|
19045
|
+
RUNTIME_DETECTED: "Runtime detected",
|
|
19046
|
+
PROVIDER_READINESS_CHECKED: "Provider readiness checked",
|
|
19047
|
+
PROVIDER_SWITCHED: "Provider switched",
|
|
19048
|
+
JOB_PROVIDER_BOUND: "Job provider bound",
|
|
19630
19049
|
// Hermes user-channel delivery only. The node and openclaw user-channel hops
|
|
19631
19050
|
// keep emitting USER_DISPATCHED / PROMPT_USER_CHECKPOINT — one name per
|
|
19632
19051
|
// transport is the whole point of splitting the send events.
|
|
@@ -19907,23 +19326,23 @@ function appendActiveXmtpTestMetric(level, eventName, extra) {
|
|
|
19907
19326
|
}
|
|
19908
19327
|
try {
|
|
19909
19328
|
const homeDir = resolveA2aTaskHome();
|
|
19910
|
-
const testsDir = (0,
|
|
19911
|
-
const activePath = (0,
|
|
19912
|
-
if (!(0,
|
|
19329
|
+
const testsDir = (0, import_node_path12.join)(homeDir, "xmtp-tests");
|
|
19330
|
+
const activePath = (0, import_node_path12.join)(testsDir, "active.json");
|
|
19331
|
+
if (!(0, import_node_fs8.existsSync)(activePath)) {
|
|
19913
19332
|
return;
|
|
19914
19333
|
}
|
|
19915
|
-
const active = JSON.parse((0,
|
|
19334
|
+
const active = JSON.parse((0, import_node_fs8.readFileSync)(activePath, "utf8"));
|
|
19916
19335
|
const runId = typeof active.runId === "string" ? active.runId : "";
|
|
19917
19336
|
if (!RUN_ID_PATTERN.test(runId)) {
|
|
19918
19337
|
return;
|
|
19919
19338
|
}
|
|
19920
|
-
const runDir = (0,
|
|
19921
|
-
const root = `${(0,
|
|
19339
|
+
const runDir = (0, import_node_path12.resolve)(testsDir, runId);
|
|
19340
|
+
const root = `${(0, import_node_path12.resolve)(testsDir)}${import_node_path12.sep}`;
|
|
19922
19341
|
if (!runDir.startsWith(root)) {
|
|
19923
19342
|
return;
|
|
19924
19343
|
}
|
|
19925
|
-
(0,
|
|
19926
|
-
(0,
|
|
19344
|
+
(0, import_node_fs8.appendFileSync)(
|
|
19345
|
+
(0, import_node_path12.join)(runDir, "events.jsonl"),
|
|
19927
19346
|
`${JSON.stringify({
|
|
19928
19347
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19929
19348
|
level,
|
|
@@ -19937,12 +19356,12 @@ function appendActiveXmtpTestMetric(level, eventName, extra) {
|
|
|
19937
19356
|
} catch {
|
|
19938
19357
|
}
|
|
19939
19358
|
}
|
|
19940
|
-
var
|
|
19359
|
+
var import_node_fs8, import_node_path12, RUN_ID_PATTERN, XMTP_TEST_EVENTS;
|
|
19941
19360
|
var init_xmtp_test_metrics = __esm({
|
|
19942
19361
|
"../core/src/xmtp-test-metrics.ts"() {
|
|
19943
19362
|
"use strict";
|
|
19944
|
-
|
|
19945
|
-
|
|
19363
|
+
import_node_fs8 = require("node:fs");
|
|
19364
|
+
import_node_path12 = require("node:path");
|
|
19946
19365
|
init_a2a_paths();
|
|
19947
19366
|
RUN_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
|
|
19948
19367
|
XMTP_TEST_EVENTS = /* @__PURE__ */ new Set([
|
|
@@ -20027,7 +19446,15 @@ function classifyError(error, code) {
|
|
|
20027
19446
|
if (normalizedCode === "ABORT_ERR" || name === "aborterror") {
|
|
20028
19447
|
return "cancelled";
|
|
20029
19448
|
}
|
|
20030
|
-
if (name === "syntaxerror"
|
|
19449
|
+
if (name === "syntaxerror") {
|
|
19450
|
+
return "invalid_response";
|
|
19451
|
+
}
|
|
19452
|
+
if (/connection\s+(?:reset|closed|interrupted)|broken\s+pipe|unexpected\s+eof/.test(
|
|
19453
|
+
message
|
|
19454
|
+
)) {
|
|
19455
|
+
return "connection_interrupted";
|
|
19456
|
+
}
|
|
19457
|
+
if (/(?:json|response).*(?:parse|invalid)/.test(message)) {
|
|
20031
19458
|
return "invalid_response";
|
|
20032
19459
|
}
|
|
20033
19460
|
if (/session\s+(?:is\s+)?expired|login\s+expired/.test(message)) {
|
|
@@ -20182,6 +19609,9 @@ function applyFatalEventDefaults(event) {
|
|
|
20182
19609
|
}
|
|
20183
19610
|
return event;
|
|
20184
19611
|
}
|
|
19612
|
+
function isInfoEventAllowlisted(name) {
|
|
19613
|
+
return SENTRY_INFO_ALLOWLIST.has(name);
|
|
19614
|
+
}
|
|
20185
19615
|
function agentExtras(identity) {
|
|
20186
19616
|
return {
|
|
20187
19617
|
walletAddress: identity.walletAddress || UNKNOWN_FIELD,
|
|
@@ -20321,6 +19751,7 @@ var init_sentry_logger = __esm({
|
|
|
20321
19751
|
"releaseBase",
|
|
20322
19752
|
"releaseChannel",
|
|
20323
19753
|
"releaseCode",
|
|
19754
|
+
"replayOutcome",
|
|
20324
19755
|
"role",
|
|
20325
19756
|
"runId",
|
|
20326
19757
|
"runtimeContainer",
|
|
@@ -20387,6 +19818,13 @@ var init_sentry_logger = __esm({
|
|
|
20387
19818
|
LogEvent.AI_RUN_STARTED,
|
|
20388
19819
|
LogEvent.AI_RUN_COMPLETED,
|
|
20389
19820
|
LogEvent.AI_RUN_SKIPPED,
|
|
19821
|
+
// Codex runtime-binding checkpoints. Omitting any of these makes info() drop
|
|
19822
|
+
// the event silently, so the whole FR-4 trace would look implemented in code
|
|
19823
|
+
// and emit nothing in production.
|
|
19824
|
+
LogEvent.RUNTIME_DETECTED,
|
|
19825
|
+
LogEvent.PROVIDER_READINESS_CHECKED,
|
|
19826
|
+
LogEvent.PROVIDER_SWITCHED,
|
|
19827
|
+
LogEvent.JOB_PROVIDER_BOUND,
|
|
20390
19828
|
LogEvent.USER_CHANNEL_MESSAGE_DELIVERED,
|
|
20391
19829
|
LogEvent.SYSTEM_NOTIFICATION_RECEIVED,
|
|
20392
19830
|
LogEvent.SYSTEM_NOTIFICATION_ROUTED,
|
|
@@ -20567,120 +20005,1637 @@ var init_sentry_logger = __esm({
|
|
|
20567
20005
|
} catch {
|
|
20568
20006
|
}
|
|
20569
20007
|
}
|
|
20570
|
-
async shutdown() {
|
|
20571
|
-
try {
|
|
20572
|
-
await Sentry.close(5e3);
|
|
20573
|
-
} catch {
|
|
20574
|
-
await Sentry.flush(5e3);
|
|
20575
|
-
}
|
|
20008
|
+
async shutdown() {
|
|
20009
|
+
try {
|
|
20010
|
+
await Sentry.close(5e3);
|
|
20011
|
+
} catch {
|
|
20012
|
+
await Sentry.flush(5e3);
|
|
20013
|
+
}
|
|
20014
|
+
}
|
|
20015
|
+
flush() {
|
|
20016
|
+
while (this.buffer.length > 0) {
|
|
20017
|
+
const entry = this.buffer.shift();
|
|
20018
|
+
if (entry.level === "info") {
|
|
20019
|
+
this.captureInfo(entry.message, entry.extra ?? {});
|
|
20020
|
+
} else {
|
|
20021
|
+
this.captureError(entry.message, entry.error, entry.extra ?? {});
|
|
20022
|
+
}
|
|
20023
|
+
}
|
|
20024
|
+
}
|
|
20025
|
+
static sanitizeExtra(extra) {
|
|
20026
|
+
return Object.fromEntries(
|
|
20027
|
+
Object.entries(extra).filter(([, value]) => value !== void 0).filter(([key]) => !_SentryLogger.isBlockedExtraKey(key)).map(([key, value]) => [key, _SentryLogger.safeValueForKey(key, value)])
|
|
20028
|
+
);
|
|
20029
|
+
}
|
|
20030
|
+
static isBlockedExtraKey(key) {
|
|
20031
|
+
const lower = key.toLowerCase();
|
|
20032
|
+
const normalized = _SentryLogger.normalizeSensitiveKey(key);
|
|
20033
|
+
if (SENTRY_EXTRA_BLOCKLIST.has(lower) || SENTRY_EXTRA_BLOCKLIST.has(normalized)) {
|
|
20034
|
+
return true;
|
|
20035
|
+
}
|
|
20036
|
+
if (_SentryLogger.isSafeMetricKey(normalized)) {
|
|
20037
|
+
return false;
|
|
20038
|
+
}
|
|
20039
|
+
return SENTRY_EXTRA_BLOCKED_KEY_PARTS.some((needle) => normalized.includes(needle));
|
|
20040
|
+
}
|
|
20041
|
+
static isSafeMetricKey(normalized) {
|
|
20042
|
+
return /(bytes|count|length|ms|size)$/.test(normalized);
|
|
20043
|
+
}
|
|
20044
|
+
static safeValueForKey(key, value) {
|
|
20045
|
+
if (typeof value === "string" && SENTRY_FULL_STRING_EXTRA_KEYS.has(_SentryLogger.normalizeSensitiveKey(key))) {
|
|
20046
|
+
return value;
|
|
20047
|
+
}
|
|
20048
|
+
return _SentryLogger.safeValue(value);
|
|
20049
|
+
}
|
|
20050
|
+
static normalizeSensitiveKey(key) {
|
|
20051
|
+
return key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
20052
|
+
}
|
|
20053
|
+
static safeValue(value) {
|
|
20054
|
+
if (typeof value === "string") {
|
|
20055
|
+
return value.length <= MAX_EXTRA_STRING_LENGTH ? value : `${value.slice(0, MAX_EXTRA_STRING_LENGTH)}...`;
|
|
20056
|
+
}
|
|
20057
|
+
if (value === null || value === void 0 || typeof value === "number" || typeof value === "boolean") {
|
|
20058
|
+
return value;
|
|
20059
|
+
}
|
|
20060
|
+
if (typeof value === "bigint") {
|
|
20061
|
+
return value.toString();
|
|
20062
|
+
}
|
|
20063
|
+
if (value instanceof Error) {
|
|
20064
|
+
return {
|
|
20065
|
+
name: value.name,
|
|
20066
|
+
messageLength: value.message.length
|
|
20067
|
+
};
|
|
20068
|
+
}
|
|
20069
|
+
if (Array.isArray(value)) {
|
|
20070
|
+
return value.slice(0, 20).map((item) => _SentryLogger.safeValue(item));
|
|
20071
|
+
}
|
|
20072
|
+
if (typeof value === "object") {
|
|
20073
|
+
return Object.fromEntries(
|
|
20074
|
+
Object.entries(value).slice(0, 50).filter(([, item]) => item !== void 0).filter(([key]) => !_SentryLogger.isBlockedExtraKey(key)).map(([key, item]) => [key, _SentryLogger.safeValueForKey(key, item)])
|
|
20075
|
+
);
|
|
20076
|
+
}
|
|
20077
|
+
const text = String(value);
|
|
20078
|
+
return text.length <= MAX_EXTRA_STRING_LENGTH ? text : `${text.slice(0, MAX_EXTRA_STRING_LENGTH)}...`;
|
|
20079
|
+
}
|
|
20080
|
+
static tagValue(value) {
|
|
20081
|
+
const text = String(value);
|
|
20082
|
+
return text.length <= MAX_TAG_VALUE_LENGTH ? text : `${text.slice(0, MAX_TAG_VALUE_LENGTH)}...`;
|
|
20083
|
+
}
|
|
20084
|
+
static normalizeTagKey(key) {
|
|
20085
|
+
return key.replace(/[^A-Za-z0-9_.:-]+/g, "_").slice(0, 32) || "unknown";
|
|
20086
|
+
}
|
|
20087
|
+
static applyDiagnostics(scope, eventName, extra) {
|
|
20088
|
+
const enriched = { eventName, ...extra };
|
|
20089
|
+
for (const [key, value] of Object.entries(enriched)) {
|
|
20090
|
+
if (!SENTRY_TAG_KEYS.has(key)) {
|
|
20091
|
+
continue;
|
|
20092
|
+
}
|
|
20093
|
+
scope.setTag(_SentryLogger.normalizeTagKey(key), _SentryLogger.tagValue(value));
|
|
20094
|
+
}
|
|
20095
|
+
const fingerprint = ["a2a", eventName];
|
|
20096
|
+
for (const key of SENTRY_FINGERPRINT_KEYS) {
|
|
20097
|
+
const value = enriched[key];
|
|
20098
|
+
if (value) {
|
|
20099
|
+
fingerprint.push(`${key}:${_SentryLogger.tagValue(value)}`);
|
|
20100
|
+
}
|
|
20101
|
+
}
|
|
20102
|
+
scope.setFingerprint(fingerprint);
|
|
20103
|
+
}
|
|
20104
|
+
static toPascalCase(str) {
|
|
20105
|
+
return str.split(/[\s:]+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
20106
|
+
}
|
|
20107
|
+
static createSentryEvent(eventName) {
|
|
20108
|
+
const err = new Error(eventName);
|
|
20109
|
+
err.name = _SentryLogger.toPascalCase(eventName);
|
|
20110
|
+
err.stack = `${err.name}: ${eventName}`;
|
|
20111
|
+
return err;
|
|
20112
|
+
}
|
|
20113
|
+
};
|
|
20114
|
+
logger = SentryLogger.getInstance();
|
|
20115
|
+
initLogger = (config) => {
|
|
20116
|
+
return logger.init(config);
|
|
20117
|
+
};
|
|
20118
|
+
shutdown = () => {
|
|
20119
|
+
return logger.shutdown();
|
|
20120
|
+
};
|
|
20121
|
+
UNKNOWN_FIELD = "unknown";
|
|
20122
|
+
}
|
|
20123
|
+
});
|
|
20124
|
+
|
|
20125
|
+
// src/ai-provider.ts
|
|
20126
|
+
function normalizeAiProvider(value) {
|
|
20127
|
+
const normalized = value.trim().toLowerCase();
|
|
20128
|
+
if (normalized === "codex") {
|
|
20129
|
+
return "codex";
|
|
20130
|
+
}
|
|
20131
|
+
if (normalized === "claude" || normalized === "claude-code" || normalized === "claude_code") {
|
|
20132
|
+
return "claude";
|
|
20133
|
+
}
|
|
20134
|
+
if (normalized === "hermes") {
|
|
20135
|
+
return "hermes";
|
|
20136
|
+
}
|
|
20137
|
+
if (normalized === "openclaw") {
|
|
20138
|
+
return "openclaw";
|
|
20139
|
+
}
|
|
20140
|
+
throw new Error(`Unsupported AI provider "${value}". Use one of: ${AI_PROVIDERS.join(", ")}.`);
|
|
20141
|
+
}
|
|
20142
|
+
function detectAiProviders(commandExists2 = commandExists, env = process.env) {
|
|
20143
|
+
const codex = commandExists2(readProviderCommand("codex", env));
|
|
20144
|
+
const claude = commandExists2(readProviderCommand("claude", env));
|
|
20145
|
+
const hermes = commandExists2(readProviderCommand("hermes", env));
|
|
20146
|
+
const openclaw = commandExists2(readProviderCommand("openclaw", env));
|
|
20147
|
+
return {
|
|
20148
|
+
codex,
|
|
20149
|
+
claude,
|
|
20150
|
+
hermes,
|
|
20151
|
+
openclaw,
|
|
20152
|
+
available: [
|
|
20153
|
+
...codex ? ["codex"] : [],
|
|
20154
|
+
...claude ? ["claude"] : [],
|
|
20155
|
+
...hermes ? ["hermes"] : [],
|
|
20156
|
+
...openclaw ? ["openclaw"] : []
|
|
20157
|
+
]
|
|
20158
|
+
};
|
|
20159
|
+
}
|
|
20160
|
+
function detectCurrentAiProvider(env = process.env) {
|
|
20161
|
+
const explicit = env.OKX_A2A_AI_PROVIDER ?? env.OKX_AGENT_TASK_AI_CLI;
|
|
20162
|
+
if (explicit) {
|
|
20163
|
+
return normalizeAiProvider(explicit);
|
|
20164
|
+
}
|
|
20165
|
+
const runtime = detectRuntime(env);
|
|
20166
|
+
return runtime === "unknown" ? null : runtime;
|
|
20167
|
+
}
|
|
20168
|
+
function hasAiRuntimeMarker(env = process.env) {
|
|
20169
|
+
return !!(detectGatewayInvocation(env) || env.CODEX_THREAD_ID || isTruthyRuntimeMarker(env.CODEX_SHELL) || env.CODEX_MANAGED_BY_NPM || isTruthyRuntimeMarker(env.CODEX_CI) || env.CLAUDE_SESSION_ID || isTruthyRuntimeMarker(env.CLAUDECODE) || env.CLAUDE_CODE_SESSION_ID || env.CLAUDE_PLUGIN_DATA || env.HERMES_SESSION_ID && !env.HERMES_DESKTOP_CWD || env.HERMES_SESSION_KEY || env.HERMES_FLOW_ID || env.OPENCLAW_SHELL || env.OPENCLAW_CLI);
|
|
20170
|
+
}
|
|
20171
|
+
function detectRuntime(env = process.env, options = {}) {
|
|
20172
|
+
const gatewayInvocation = detectGatewayInvocation(env);
|
|
20173
|
+
const strongMatches = [
|
|
20174
|
+
...gatewayInvocation ? [gatewayInvocation] : [],
|
|
20175
|
+
...env.CODEX_THREAD_ID || isTruthyRuntimeMarker(env.CODEX_SHELL) || env.CODEX_MANAGED_BY_NPM || isTruthyRuntimeMarker(env.CODEX_CI) ? ["codex"] : [],
|
|
20176
|
+
...env.CLAUDE_SESSION_ID || isTruthyRuntimeMarker(env.CLAUDECODE) || env.CLAUDE_CODE_SESSION_ID || env.CLAUDE_PLUGIN_DATA ? ["claude"] : [],
|
|
20177
|
+
...env.HERMES_SESSION_ID && !env.HERMES_DESKTOP_CWD || env.HERMES_SESSION_KEY || env.HERMES_FLOW_ID ? ["hermes"] : [],
|
|
20178
|
+
...env.OPENCLAW_SHELL || env.OPENCLAW_CLI ? ["openclaw"] : [],
|
|
20179
|
+
...hasOpenClawParentProcess(options.parentPid ?? process.ppid, options.readParentProcessCommand ?? readParentProcessCommand) ? ["openclaw"] : []
|
|
20180
|
+
];
|
|
20181
|
+
const uniqueMatches = [...new Set(strongMatches)];
|
|
20182
|
+
if (uniqueMatches.length === 1) {
|
|
20183
|
+
return uniqueMatches[0];
|
|
20184
|
+
}
|
|
20185
|
+
return "unknown";
|
|
20186
|
+
}
|
|
20187
|
+
function detectGatewayInvocation(env = process.env) {
|
|
20188
|
+
const context = (env.OKX_A2A_RUNTIME_CONTEXT ?? "").trim().toLowerCase();
|
|
20189
|
+
if (context === "openclaw-gateway" || context === "gateway:openclaw") {
|
|
20190
|
+
return "openclaw";
|
|
20191
|
+
}
|
|
20192
|
+
if (context === "hermes-gateway" || context === "gateway:hermes") {
|
|
20193
|
+
return "hermes";
|
|
20194
|
+
}
|
|
20195
|
+
if (isTruthyRuntimeMarker(env._HERMES_GATEWAY)) {
|
|
20196
|
+
return "hermes";
|
|
20197
|
+
}
|
|
20198
|
+
if ((env.OPENCLAW_SERVICE_KIND ?? "").trim().toLowerCase() === "gateway") {
|
|
20199
|
+
return "openclaw";
|
|
20200
|
+
}
|
|
20201
|
+
if (!isTruthyRuntimeMarker(env.OKX_A2A_IN_GATEWAY)) {
|
|
20202
|
+
return null;
|
|
20203
|
+
}
|
|
20204
|
+
const provider = env.OKX_A2A_GATEWAY_PROVIDER;
|
|
20205
|
+
if (!provider) {
|
|
20206
|
+
return null;
|
|
20207
|
+
}
|
|
20208
|
+
try {
|
|
20209
|
+
const normalized = normalizeAiProvider(provider);
|
|
20210
|
+
return normalized === "openclaw" || normalized === "hermes" ? normalized : null;
|
|
20211
|
+
} catch {
|
|
20212
|
+
return null;
|
|
20213
|
+
}
|
|
20214
|
+
}
|
|
20215
|
+
function switchProvider(options) {
|
|
20216
|
+
const runtime = detectRuntime(options.env ?? process.env, {
|
|
20217
|
+
parentPid: options.parentPid,
|
|
20218
|
+
readParentProcessCommand: options.readParentProcessCommand
|
|
20219
|
+
});
|
|
20220
|
+
if (runtime === "unknown") {
|
|
20221
|
+
const stored = options.store.getDefaultAiProvider();
|
|
20222
|
+
if (stored) {
|
|
20223
|
+
return {
|
|
20224
|
+
ok: true,
|
|
20225
|
+
runtime: "unknown",
|
|
20226
|
+
provider: stored,
|
|
20227
|
+
previousProvider: stored,
|
|
20228
|
+
changed: false,
|
|
20229
|
+
action: "kept_existing",
|
|
20230
|
+
userMessage: `No AI runtime detected for the current environment; keeping the existing default provider (${formatAiProviderDisplayName(stored)}).`
|
|
20231
|
+
};
|
|
20232
|
+
}
|
|
20233
|
+
return {
|
|
20234
|
+
ok: false,
|
|
20235
|
+
runtime: "unknown",
|
|
20236
|
+
reason: "unknown_runtime",
|
|
20237
|
+
state: "blocked",
|
|
20238
|
+
message: "Could not determine the current AI provider.",
|
|
20239
|
+
userMessage: "Could not detect the current AI runtime and no default AI provider is configured. Run this from your AI environment (OpenClaw / Codex / Claude / Hermes), or set a provider manually: `okx-a2a ai-provider set --provider <codex|claude|hermes|openclaw>`."
|
|
20240
|
+
};
|
|
20241
|
+
}
|
|
20242
|
+
const previousProvider = options.store.getDefaultAiProvider();
|
|
20243
|
+
const changed = previousProvider !== runtime;
|
|
20244
|
+
options.store.setDefaultAiProvider(runtime);
|
|
20245
|
+
return {
|
|
20246
|
+
ok: true,
|
|
20247
|
+
runtime,
|
|
20248
|
+
provider: runtime,
|
|
20249
|
+
previousProvider,
|
|
20250
|
+
changed,
|
|
20251
|
+
action: "switched",
|
|
20252
|
+
userMessage: changed ? `Switched to ${formatAiProviderDisplayName(runtime)}\uFF5CNew tasks will be handled here; unfinished tasks should be continued in their original session` : ""
|
|
20253
|
+
};
|
|
20254
|
+
}
|
|
20255
|
+
function formatAiProviderDisplayName(provider) {
|
|
20256
|
+
if (provider === "openclaw") {
|
|
20257
|
+
return "OpenClaw";
|
|
20258
|
+
}
|
|
20259
|
+
return `${provider.charAt(0).toUpperCase()}${provider.slice(1)}`;
|
|
20260
|
+
}
|
|
20261
|
+
function isTruthyRuntimeMarker(value) {
|
|
20262
|
+
return value === "1" || value === "true" || value === "yes";
|
|
20263
|
+
}
|
|
20264
|
+
function hasOpenClawParentProcess(parentPid, readParentProcessCommand2) {
|
|
20265
|
+
let pid = parentPid;
|
|
20266
|
+
for (let i = 0; i < 8; i++) {
|
|
20267
|
+
if (!pid || pid <= 1) {
|
|
20268
|
+
return false;
|
|
20269
|
+
}
|
|
20270
|
+
const processInfo = readParentProcessCommand2(pid);
|
|
20271
|
+
if (!processInfo) {
|
|
20272
|
+
return false;
|
|
20273
|
+
}
|
|
20274
|
+
if (/openclaw/i.test(processInfo.command)) {
|
|
20275
|
+
return true;
|
|
20276
|
+
}
|
|
20277
|
+
if (!processInfo.parentPid || processInfo.parentPid === pid) {
|
|
20278
|
+
return false;
|
|
20279
|
+
}
|
|
20280
|
+
pid = processInfo.parentPid;
|
|
20281
|
+
}
|
|
20282
|
+
return false;
|
|
20283
|
+
}
|
|
20284
|
+
function readParentProcessCommand(pid) {
|
|
20285
|
+
return readParentProcessCommandForPlatform(pid, process.platform);
|
|
20286
|
+
}
|
|
20287
|
+
function readParentProcessCommandForPlatform(pid, platform) {
|
|
20288
|
+
if (platform === "win32") {
|
|
20289
|
+
return readWindowsParentProcessCommand(pid);
|
|
20290
|
+
}
|
|
20291
|
+
const commandResult = (0, import_node_child_process5.spawnSync)("ps", ["-p", String(pid), "-o", "comm="], {
|
|
20292
|
+
encoding: "utf8",
|
|
20293
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
20294
|
+
});
|
|
20295
|
+
const parentResult = (0, import_node_child_process5.spawnSync)("ps", ["-p", String(pid), "-o", "ppid="], {
|
|
20296
|
+
encoding: "utf8",
|
|
20297
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
20298
|
+
});
|
|
20299
|
+
const command = typeof commandResult.stdout === "string" ? commandResult.stdout.trim() : "";
|
|
20300
|
+
const parentPidRaw = typeof parentResult.stdout === "string" ? parentResult.stdout.trim() : "";
|
|
20301
|
+
const parentPid = Number(parentPidRaw);
|
|
20302
|
+
if (commandResult.status !== 0 || parentResult.status !== 0 || !command || !Number.isFinite(parentPid)) {
|
|
20303
|
+
return null;
|
|
20304
|
+
}
|
|
20305
|
+
return { command, parentPid };
|
|
20306
|
+
}
|
|
20307
|
+
function buildWindowsParentProcessProbe(pid) {
|
|
20308
|
+
if (!Number.isInteger(pid)) {
|
|
20309
|
+
return null;
|
|
20310
|
+
}
|
|
20311
|
+
return {
|
|
20312
|
+
command: "powershell.exe",
|
|
20313
|
+
args: [
|
|
20314
|
+
"-NoProfile",
|
|
20315
|
+
"-NonInteractive",
|
|
20316
|
+
"-Command",
|
|
20317
|
+
`Get-CimInstance Win32_Process -Filter "ProcessId=${pid}" | Select-Object Name,ParentProcessId | ConvertTo-Json -Compress`
|
|
20318
|
+
]
|
|
20319
|
+
};
|
|
20320
|
+
}
|
|
20321
|
+
function parseWindowsParentProcessJson(stdout) {
|
|
20322
|
+
const trimmed = stdout.trim();
|
|
20323
|
+
if (!trimmed) {
|
|
20324
|
+
return null;
|
|
20325
|
+
}
|
|
20326
|
+
let parsed;
|
|
20327
|
+
try {
|
|
20328
|
+
parsed = JSON.parse(trimmed);
|
|
20329
|
+
} catch {
|
|
20330
|
+
return null;
|
|
20331
|
+
}
|
|
20332
|
+
const record = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
20333
|
+
if (!record || typeof record !== "object") {
|
|
20334
|
+
return null;
|
|
20335
|
+
}
|
|
20336
|
+
const name = record.Name;
|
|
20337
|
+
if (typeof name !== "string" || name.trim() === "") {
|
|
20338
|
+
return null;
|
|
20339
|
+
}
|
|
20340
|
+
const rawParentPid = Number(record.ParentProcessId);
|
|
20341
|
+
const parentPid = Number.isInteger(rawParentPid) && rawParentPid > 0 ? rawParentPid : null;
|
|
20342
|
+
return { command: name.trim(), parentPid };
|
|
20343
|
+
}
|
|
20344
|
+
function readWindowsParentProcessCommand(pid) {
|
|
20345
|
+
const probe = buildWindowsParentProcessProbe(pid);
|
|
20346
|
+
if (!probe) {
|
|
20347
|
+
logWinCompat(`${WIN_COMPAT_LOG_PREFIX} parent-process probe rejected non-integer pid=${String(pid)}`);
|
|
20348
|
+
return null;
|
|
20349
|
+
}
|
|
20350
|
+
const result = (0, import_node_child_process5.spawnSync)(probe.command, probe.args, {
|
|
20351
|
+
encoding: "utf8",
|
|
20352
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
20353
|
+
timeout: 5e3,
|
|
20354
|
+
windowsHide: true
|
|
20355
|
+
});
|
|
20356
|
+
if (result.error) {
|
|
20357
|
+
const spawnError = result.error;
|
|
20358
|
+
logWinCompat(
|
|
20359
|
+
`${WIN_COMPAT_LOG_PREFIX} parent-process probe failed pid=${pid} command=${probe.command} errorCode=${spawnError.code ?? "unknown"} error=${spawnError.message}`
|
|
20360
|
+
);
|
|
20361
|
+
return null;
|
|
20362
|
+
}
|
|
20363
|
+
if (result.status !== 0) {
|
|
20364
|
+
logWinCompat(
|
|
20365
|
+
`${WIN_COMPAT_LOG_PREFIX} parent-process probe exited pid=${pid} command=${probe.command} status=${String(result.status)} signal=${String(result.signal)}`
|
|
20366
|
+
);
|
|
20367
|
+
return null;
|
|
20368
|
+
}
|
|
20369
|
+
const stdout = typeof result.stdout === "string" ? result.stdout : "";
|
|
20370
|
+
const parsed = parseWindowsParentProcessJson(stdout);
|
|
20371
|
+
if (!parsed) {
|
|
20372
|
+
logWinCompat(
|
|
20373
|
+
`${WIN_COMPAT_LOG_PREFIX} parent-process probe unparsable output pid=${pid} stdout=${JSON.stringify(stdout.slice(0, 200))}`
|
|
20374
|
+
);
|
|
20375
|
+
return null;
|
|
20376
|
+
}
|
|
20377
|
+
if (process.env.OKX_A2A_WIN_COMPAT_VERBOSE === "1") {
|
|
20378
|
+
logWinCompat(
|
|
20379
|
+
`${WIN_COMPAT_LOG_PREFIX} parent-process probe pid=${pid} -> command=${parsed.command} parentPid=${parsed.parentPid ?? "null"}`
|
|
20380
|
+
);
|
|
20381
|
+
}
|
|
20382
|
+
return parsed;
|
|
20383
|
+
}
|
|
20384
|
+
async function ensureDefaultAiProvider(options) {
|
|
20385
|
+
const env = options.env ?? process.env;
|
|
20386
|
+
const commandExists2 = options.commandExists ?? commandExists;
|
|
20387
|
+
const detection = withStoredCodexAvailability(
|
|
20388
|
+
detectAiProviders(commandExists2, env),
|
|
20389
|
+
options.store,
|
|
20390
|
+
commandExists2,
|
|
20391
|
+
env
|
|
20392
|
+
);
|
|
20393
|
+
if (options.requestedProvider) {
|
|
20394
|
+
const provider = normalizeAiProvider(options.requestedProvider);
|
|
20395
|
+
if (!options.allowUnavailableProvider) {
|
|
20396
|
+
assertInstalled(provider, detection);
|
|
20397
|
+
}
|
|
20398
|
+
options.store.setDefaultAiProvider(provider);
|
|
20399
|
+
return {
|
|
20400
|
+
provider,
|
|
20401
|
+
source: "requested",
|
|
20402
|
+
message: isInstalled(provider, detection) ? `default AI provider set to ${provider}` : `default AI provider set to ${provider}; availability was not verified on PATH`
|
|
20403
|
+
};
|
|
20404
|
+
}
|
|
20405
|
+
if (options.forcePrompt) {
|
|
20406
|
+
const provider = await promptForAiProvider({
|
|
20407
|
+
detection,
|
|
20408
|
+
stdin: options.stdin,
|
|
20409
|
+
stdout: options.stdout,
|
|
20410
|
+
currentProvider: options.store.getDefaultAiProvider()
|
|
20411
|
+
});
|
|
20412
|
+
options.store.setDefaultAiProvider(provider);
|
|
20413
|
+
return {
|
|
20414
|
+
provider,
|
|
20415
|
+
source: "detected",
|
|
20416
|
+
message: `default AI provider set to ${provider}`
|
|
20417
|
+
};
|
|
20418
|
+
}
|
|
20419
|
+
if (env.OKX_AGENT_TASK_AI_CLI) {
|
|
20420
|
+
const provider = normalizeAiProvider(env.OKX_AGENT_TASK_AI_CLI);
|
|
20421
|
+
if (!options.allowUnavailableProvider) {
|
|
20422
|
+
assertInstalled(provider, detection);
|
|
20423
|
+
}
|
|
20424
|
+
options.store.setDefaultAiProvider(provider);
|
|
20425
|
+
return {
|
|
20426
|
+
provider,
|
|
20427
|
+
source: "env",
|
|
20428
|
+
message: isInstalled(provider, detection) ? `default AI provider set to ${provider} from OKX_AGENT_TASK_AI_CLI` : `default AI provider set to ${provider} from OKX_AGENT_TASK_AI_CLI; availability was not verified on PATH`
|
|
20429
|
+
};
|
|
20430
|
+
}
|
|
20431
|
+
if (env.OKX_A2A_AI_PROVIDER) {
|
|
20432
|
+
const provider = normalizeAiProvider(env.OKX_A2A_AI_PROVIDER);
|
|
20433
|
+
if (!options.allowUnavailableProvider) {
|
|
20434
|
+
assertInstalled(provider, detection);
|
|
20435
|
+
}
|
|
20436
|
+
return {
|
|
20437
|
+
provider,
|
|
20438
|
+
source: "env",
|
|
20439
|
+
message: isInstalled(provider, detection) ? `AI provider resolved from OKX_A2A_AI_PROVIDER: ${provider}` : `AI provider resolved from OKX_A2A_AI_PROVIDER: ${provider}; availability was not verified on PATH`
|
|
20440
|
+
};
|
|
20441
|
+
}
|
|
20442
|
+
const stored = options.store.getDefaultAiProvider();
|
|
20443
|
+
if (stored && (options.allowUnavailableProvider || isInstalled(stored, detection))) {
|
|
20444
|
+
return {
|
|
20445
|
+
provider: stored,
|
|
20446
|
+
source: "stored",
|
|
20447
|
+
message: isInstalled(stored, detection) ? `default AI provider is ${stored}` : `default AI provider is ${stored}; availability was not verified on PATH`
|
|
20448
|
+
};
|
|
20449
|
+
}
|
|
20450
|
+
if (stored) {
|
|
20451
|
+
options.stderr?.write(
|
|
20452
|
+
`[okx-agent-task] stored AI provider "${stored}" is not installed or not on PATH; selecting again.
|
|
20453
|
+
`
|
|
20454
|
+
);
|
|
20455
|
+
}
|
|
20456
|
+
if (options.promptIfMissing) {
|
|
20457
|
+
const provider = await promptForAiProvider({
|
|
20458
|
+
detection,
|
|
20459
|
+
stdin: options.stdin,
|
|
20460
|
+
stdout: options.stdout,
|
|
20461
|
+
currentProvider: stored ?? null
|
|
20462
|
+
});
|
|
20463
|
+
options.store.setDefaultAiProvider(provider);
|
|
20464
|
+
return {
|
|
20465
|
+
provider,
|
|
20466
|
+
source: "detected",
|
|
20467
|
+
message: `default AI provider set to ${provider}`
|
|
20468
|
+
};
|
|
20469
|
+
}
|
|
20470
|
+
const current = detectCurrentAiProvider(env);
|
|
20471
|
+
if (current && (options.allowUnavailableProvider || isInstalled(current, detection))) {
|
|
20472
|
+
return {
|
|
20473
|
+
provider: current,
|
|
20474
|
+
source: "env",
|
|
20475
|
+
message: isInstalled(current, detection) ? `AI provider resolved from current environment: ${current}` : `AI provider resolved from current environment: ${current}; availability was not verified on PATH`
|
|
20476
|
+
};
|
|
20477
|
+
}
|
|
20478
|
+
if (detection.available.length === 0) {
|
|
20479
|
+
throw new Error(
|
|
20480
|
+
`No supported AI CLI found. Install one of: ${AI_PROVIDERS.join(", ")}.`
|
|
20481
|
+
);
|
|
20482
|
+
}
|
|
20483
|
+
if (detection.available.length === 1) {
|
|
20484
|
+
const provider = detection.available[0];
|
|
20485
|
+
options.store.setDefaultAiProvider(provider);
|
|
20486
|
+
return {
|
|
20487
|
+
provider,
|
|
20488
|
+
source: "detected",
|
|
20489
|
+
message: `detected only ${provider}; default AI provider set to ${provider}`
|
|
20490
|
+
};
|
|
20491
|
+
}
|
|
20492
|
+
throw new Error(
|
|
20493
|
+
`Multiple AI CLIs are installed (${detection.available.join(", ")}) but no current AI environment was detected. Start the daemon from an AI session, pass --ai-provider <provider>, or set OKX_A2A_AI_PROVIDER.`
|
|
20494
|
+
);
|
|
20495
|
+
}
|
|
20496
|
+
function resolveConfiguredAiProvider(options) {
|
|
20497
|
+
const env = options.env ?? process.env;
|
|
20498
|
+
const explicit = env.OKX_AGENT_TASK_AI_CLI ?? env.OKX_A2A_AI_PROVIDER;
|
|
20499
|
+
if (explicit) {
|
|
20500
|
+
return normalizeAiProvider(explicit);
|
|
20501
|
+
}
|
|
20502
|
+
return options.store.getDefaultAiProvider();
|
|
20503
|
+
}
|
|
20504
|
+
function resolveConfiguredAiProviderForJob(options) {
|
|
20505
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20506
|
+
if (jobId) {
|
|
20507
|
+
const binding = options.store.getJobProviderBinding(jobId);
|
|
20508
|
+
if (binding) {
|
|
20509
|
+
bindHermesJobRouteFromEnvIfAvailable(options.store, {
|
|
20510
|
+
jobId,
|
|
20511
|
+
provider: binding.provider,
|
|
20512
|
+
env: options.env
|
|
20513
|
+
});
|
|
20514
|
+
return binding.provider;
|
|
20515
|
+
}
|
|
20516
|
+
}
|
|
20517
|
+
const provider = resolveConfiguredAiProvider({
|
|
20518
|
+
store: options.store,
|
|
20519
|
+
env: options.env
|
|
20520
|
+
});
|
|
20521
|
+
if (jobId && provider) {
|
|
20522
|
+
return bindJobProviderWithRouteIfAvailable(options.store, {
|
|
20523
|
+
jobId,
|
|
20524
|
+
provider,
|
|
20525
|
+
env: options.env
|
|
20526
|
+
}).binding.provider;
|
|
20527
|
+
}
|
|
20528
|
+
return provider;
|
|
20529
|
+
}
|
|
20530
|
+
function bindJobProviderToCurrentDefaultIfMissing(options) {
|
|
20531
|
+
const store = options.store ?? null;
|
|
20532
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20533
|
+
if (!store || !jobId) {
|
|
20534
|
+
return null;
|
|
20535
|
+
}
|
|
20536
|
+
const existing = store.getJobProviderBinding(jobId);
|
|
20537
|
+
if (existing) {
|
|
20538
|
+
bindHermesJobRouteFromEnvIfAvailable(store, {
|
|
20539
|
+
jobId,
|
|
20540
|
+
provider: existing.provider,
|
|
20541
|
+
env: options.env
|
|
20542
|
+
});
|
|
20543
|
+
return {
|
|
20544
|
+
provider: existing.provider,
|
|
20545
|
+
created: false
|
|
20546
|
+
};
|
|
20547
|
+
}
|
|
20548
|
+
const provider = store.getDefaultAiProvider();
|
|
20549
|
+
if (!provider) {
|
|
20550
|
+
return null;
|
|
20551
|
+
}
|
|
20552
|
+
const result = bindJobProviderWithRouteIfAvailable(store, {
|
|
20553
|
+
jobId,
|
|
20554
|
+
provider,
|
|
20555
|
+
env: options.env
|
|
20556
|
+
});
|
|
20557
|
+
return {
|
|
20558
|
+
provider: result.binding.provider,
|
|
20559
|
+
created: result.created
|
|
20560
|
+
};
|
|
20561
|
+
}
|
|
20562
|
+
function bindJobProviderToCurrentRuntimeIfMissing(options) {
|
|
20563
|
+
const store = options.store ?? null;
|
|
20564
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20565
|
+
if (!store || !jobId) {
|
|
20566
|
+
return {
|
|
20567
|
+
ok: false,
|
|
20568
|
+
jobId: jobId ?? "",
|
|
20569
|
+
provider: null,
|
|
20570
|
+
currentProvider: null,
|
|
20571
|
+
created: false,
|
|
20572
|
+
binding: null,
|
|
20573
|
+
reason: "missing_job_id"
|
|
20574
|
+
};
|
|
20575
|
+
}
|
|
20576
|
+
const existing = store.getJobProviderBinding(jobId);
|
|
20577
|
+
if (existing) {
|
|
20578
|
+
const binding = bindHermesJobRouteFromEnvIfAvailable(store, {
|
|
20579
|
+
jobId,
|
|
20580
|
+
provider: existing.provider,
|
|
20581
|
+
env: options.env
|
|
20582
|
+
}) ?? existing;
|
|
20583
|
+
return {
|
|
20584
|
+
ok: true,
|
|
20585
|
+
jobId,
|
|
20586
|
+
provider: binding.provider,
|
|
20587
|
+
currentProvider: null,
|
|
20588
|
+
created: false,
|
|
20589
|
+
binding,
|
|
20590
|
+
reason: "already_bound"
|
|
20591
|
+
};
|
|
20592
|
+
}
|
|
20593
|
+
const switched = switchProvider({ store, env: options.env ?? process.env });
|
|
20594
|
+
if (!switched.ok || !switched.provider) {
|
|
20595
|
+
return {
|
|
20596
|
+
ok: false,
|
|
20597
|
+
jobId,
|
|
20598
|
+
provider: null,
|
|
20599
|
+
currentProvider: null,
|
|
20600
|
+
created: false,
|
|
20601
|
+
binding: null,
|
|
20602
|
+
reason: "unknown_runtime"
|
|
20603
|
+
};
|
|
20604
|
+
}
|
|
20605
|
+
const currentProvider = switched.provider;
|
|
20606
|
+
const result = bindJobProviderWithRouteIfAvailable(store, {
|
|
20607
|
+
jobId,
|
|
20608
|
+
provider: currentProvider,
|
|
20609
|
+
env: options.env
|
|
20610
|
+
});
|
|
20611
|
+
return {
|
|
20612
|
+
ok: true,
|
|
20613
|
+
jobId,
|
|
20614
|
+
provider: result.binding.provider,
|
|
20615
|
+
currentProvider,
|
|
20616
|
+
created: result.created,
|
|
20617
|
+
binding: result.binding,
|
|
20618
|
+
reason: result.created ? "created" : "already_bound"
|
|
20619
|
+
};
|
|
20620
|
+
}
|
|
20621
|
+
async function ensureProviderReadyForBinding(options) {
|
|
20622
|
+
if (options.provider !== "codex") {
|
|
20623
|
+
return notRequiredReadinessGate(options.provider);
|
|
20624
|
+
}
|
|
20625
|
+
const check = options.readiness ?? checkCodexReadiness;
|
|
20626
|
+
const result = await check({
|
|
20627
|
+
store: options.store ?? null,
|
|
20628
|
+
env: options.env ?? process.env,
|
|
20629
|
+
platform: options.platform,
|
|
20630
|
+
persistOverrideCommand: options.persistOverrideCommand
|
|
20631
|
+
});
|
|
20632
|
+
return {
|
|
20633
|
+
provider: "codex",
|
|
20634
|
+
ready: result.ready,
|
|
20635
|
+
state: result.state,
|
|
20636
|
+
commandSource: result.commandSource,
|
|
20637
|
+
commandPersisted: result.commandPersisted,
|
|
20638
|
+
recoveryAction: result.recoveryAction,
|
|
20639
|
+
recoveryGuidance: result.recoveryGuidance
|
|
20640
|
+
};
|
|
20641
|
+
}
|
|
20642
|
+
async function switchProviderWithReadinessGateCore(options) {
|
|
20643
|
+
const env = options.env ?? process.env;
|
|
20644
|
+
const runtime = detectRuntime(env, {
|
|
20645
|
+
parentPid: options.parentPid,
|
|
20646
|
+
readParentProcessCommand: options.readParentProcessCommand
|
|
20647
|
+
});
|
|
20648
|
+
const delegate = () => switchProvider({
|
|
20649
|
+
store: options.store,
|
|
20650
|
+
env,
|
|
20651
|
+
parentPid: options.parentPid,
|
|
20652
|
+
readParentProcessCommand: options.readParentProcessCommand
|
|
20653
|
+
});
|
|
20654
|
+
if (runtime !== "codex") {
|
|
20655
|
+
const result2 = delegate();
|
|
20656
|
+
return {
|
|
20657
|
+
result: result2.ok ? result2 : { ...result2, reasonCode: "runtime_undetectable" },
|
|
20658
|
+
runtime,
|
|
20659
|
+
readiness: null
|
|
20660
|
+
};
|
|
20661
|
+
}
|
|
20662
|
+
const readiness = await ensureProviderReadyForBinding({
|
|
20663
|
+
provider: "codex",
|
|
20664
|
+
store: options.store,
|
|
20665
|
+
env,
|
|
20666
|
+
platform: options.platform,
|
|
20667
|
+
readiness: options.readiness
|
|
20668
|
+
});
|
|
20669
|
+
if (!readiness.ready) {
|
|
20670
|
+
const previousProvider = options.store.getDefaultAiProvider();
|
|
20671
|
+
const keepingClause = previousProvider ? `keeping ${formatAiProviderDisplayName(previousProvider)} as the default provider` : "the default provider was not changed";
|
|
20672
|
+
return {
|
|
20673
|
+
result: {
|
|
20674
|
+
ok: false,
|
|
20675
|
+
runtime,
|
|
20676
|
+
reason: "provider_not_ready",
|
|
20677
|
+
reasonCode: "provider_not_ready",
|
|
20678
|
+
state: "blocked",
|
|
20679
|
+
message: `Codex is not ready (${readiness.state}); the default AI provider was not changed.`,
|
|
20680
|
+
userMessage: `Codex is not ready (${readiness.state}); ${keepingClause}. ${readiness.recoveryGuidance}`,
|
|
20681
|
+
previousProvider,
|
|
20682
|
+
detectedProvider: "codex",
|
|
20683
|
+
effectiveProvider: previousProvider,
|
|
20684
|
+
readinessState: readiness.state,
|
|
20685
|
+
commandSource: readiness.commandSource,
|
|
20686
|
+
recoveryAction: readiness.recoveryAction,
|
|
20687
|
+
recoveryGuidance: readiness.recoveryGuidance
|
|
20688
|
+
},
|
|
20689
|
+
runtime,
|
|
20690
|
+
readiness
|
|
20691
|
+
};
|
|
20692
|
+
}
|
|
20693
|
+
const result = delegate();
|
|
20694
|
+
if (!result.ok) {
|
|
20695
|
+
return { result, runtime, readiness };
|
|
20696
|
+
}
|
|
20697
|
+
return {
|
|
20698
|
+
result: {
|
|
20699
|
+
...result,
|
|
20700
|
+
detectedProvider: "codex",
|
|
20701
|
+
effectiveProvider: result.provider,
|
|
20702
|
+
readinessState: readiness.state,
|
|
20703
|
+
commandSource: readiness.commandSource,
|
|
20704
|
+
commandPersisted: readiness.commandPersisted
|
|
20705
|
+
},
|
|
20706
|
+
runtime,
|
|
20707
|
+
readiness
|
|
20708
|
+
};
|
|
20709
|
+
}
|
|
20710
|
+
function classifyJobBindingSource(env = process.env, options = {}) {
|
|
20711
|
+
if (env.OKX_AGENT_TASK_AI_CLI ?? env.OKX_A2A_AI_PROVIDER) {
|
|
20712
|
+
return "explicit_env";
|
|
20713
|
+
}
|
|
20714
|
+
return detectRuntime(env, options) === "unknown" ? "default_provider" : "current_runtime";
|
|
20715
|
+
}
|
|
20716
|
+
async function bindJobProviderToCurrentDefaultIfReady(options) {
|
|
20717
|
+
const store = options.store ?? null;
|
|
20718
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20719
|
+
if (!store || !jobId) {
|
|
20720
|
+
return null;
|
|
20721
|
+
}
|
|
20722
|
+
const existing = store.getJobProviderBinding(jobId);
|
|
20723
|
+
if (existing) {
|
|
20724
|
+
return alreadyBoundGateResult(store, jobId, existing.provider, options.env);
|
|
20725
|
+
}
|
|
20726
|
+
const provider = store.getDefaultAiProvider();
|
|
20727
|
+
if (!provider) {
|
|
20728
|
+
return null;
|
|
20729
|
+
}
|
|
20730
|
+
return await bindResolvedJobProviderIfReady({
|
|
20731
|
+
store,
|
|
20732
|
+
jobId,
|
|
20733
|
+
provider,
|
|
20734
|
+
bindingSource: "default_provider",
|
|
20735
|
+
env: options.env,
|
|
20736
|
+
platform: options.platform,
|
|
20737
|
+
readiness: options.readiness
|
|
20738
|
+
});
|
|
20739
|
+
}
|
|
20740
|
+
async function bindJobProviderToCurrentRuntimeIfReady(options) {
|
|
20741
|
+
const store = options.store ?? null;
|
|
20742
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20743
|
+
if (!store || !jobId) {
|
|
20744
|
+
return {
|
|
20745
|
+
ok: false,
|
|
20746
|
+
jobId: jobId ?? "",
|
|
20747
|
+
provider: null,
|
|
20748
|
+
currentProvider: null,
|
|
20749
|
+
created: false,
|
|
20750
|
+
degraded: false,
|
|
20751
|
+
bindingSource: null,
|
|
20752
|
+
readiness: null,
|
|
20753
|
+
reason: "missing_job_id"
|
|
20754
|
+
};
|
|
20755
|
+
}
|
|
20756
|
+
const existing = store.getJobProviderBinding(jobId);
|
|
20757
|
+
if (existing) {
|
|
20758
|
+
return alreadyBoundGateResult(store, jobId, existing.provider, options.env);
|
|
20759
|
+
}
|
|
20760
|
+
const env = options.env ?? process.env;
|
|
20761
|
+
const explicit = env.OKX_AGENT_TASK_AI_CLI ?? env.OKX_A2A_AI_PROVIDER;
|
|
20762
|
+
if (explicit) {
|
|
20763
|
+
return await bindResolvedJobProviderIfReady({
|
|
20764
|
+
store,
|
|
20765
|
+
jobId,
|
|
20766
|
+
provider: normalizeAiProvider(explicit),
|
|
20767
|
+
bindingSource: "explicit_env",
|
|
20768
|
+
env,
|
|
20769
|
+
platform: options.platform,
|
|
20770
|
+
readiness: options.readiness
|
|
20771
|
+
});
|
|
20772
|
+
}
|
|
20773
|
+
const switched = await switchProviderWithReadinessGateCore({
|
|
20774
|
+
store,
|
|
20775
|
+
env,
|
|
20776
|
+
platform: options.platform,
|
|
20777
|
+
readiness: options.readiness
|
|
20778
|
+
});
|
|
20779
|
+
if (!switched.result.ok) {
|
|
20780
|
+
const notReady = switched.result.reason === "provider_not_ready";
|
|
20781
|
+
if (switched.readiness) {
|
|
20782
|
+
logProviderReadinessChecked(jobId, switched.readiness);
|
|
20783
|
+
}
|
|
20784
|
+
return {
|
|
20785
|
+
ok: false,
|
|
20786
|
+
jobId,
|
|
20787
|
+
provider: null,
|
|
20788
|
+
currentProvider: switched.result.detectedProvider ?? null,
|
|
20789
|
+
created: false,
|
|
20790
|
+
degraded: notReady,
|
|
20791
|
+
bindingSource: null,
|
|
20792
|
+
readiness: switched.readiness,
|
|
20793
|
+
reason: notReady ? "provider_not_ready" : "unknown_runtime"
|
|
20794
|
+
};
|
|
20795
|
+
}
|
|
20796
|
+
return await bindResolvedJobProviderIfReady({
|
|
20797
|
+
store,
|
|
20798
|
+
jobId,
|
|
20799
|
+
provider: switched.result.provider,
|
|
20800
|
+
bindingSource: switched.runtime === "unknown" ? "default_provider" : "current_runtime",
|
|
20801
|
+
env,
|
|
20802
|
+
platform: options.platform,
|
|
20803
|
+
readiness: options.readiness,
|
|
20804
|
+
// Reuse the gate the switch already evaluated instead of probing twice.
|
|
20805
|
+
gate: switched.readiness
|
|
20806
|
+
});
|
|
20807
|
+
}
|
|
20808
|
+
function persistDefaultProviderIfChanged(store, provider) {
|
|
20809
|
+
if (store.getDefaultAiProvider() === provider) {
|
|
20810
|
+
return false;
|
|
20811
|
+
}
|
|
20812
|
+
store.setDefaultAiProvider(provider);
|
|
20813
|
+
return true;
|
|
20814
|
+
}
|
|
20815
|
+
function resolveNewJobProviderCandidate(store, env) {
|
|
20816
|
+
const explicit = env.OKX_AGENT_TASK_AI_CLI ?? env.OKX_A2A_AI_PROVIDER;
|
|
20817
|
+
if (explicit) {
|
|
20818
|
+
return { provider: normalizeAiProvider(explicit), bindingSource: "explicit_env" };
|
|
20819
|
+
}
|
|
20820
|
+
const stored = store.getDefaultAiProvider();
|
|
20821
|
+
return stored ? { provider: stored, bindingSource: "default_provider" } : null;
|
|
20822
|
+
}
|
|
20823
|
+
async function bindNewJobProviderIfReady(options) {
|
|
20824
|
+
const store = options.store ?? null;
|
|
20825
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20826
|
+
if (!store || !jobId) {
|
|
20827
|
+
return null;
|
|
20828
|
+
}
|
|
20829
|
+
const existing = store.getJobProviderBinding(jobId);
|
|
20830
|
+
if (existing) {
|
|
20831
|
+
return alreadyBoundGateResult(store, jobId, existing.provider, options.env);
|
|
20832
|
+
}
|
|
20833
|
+
const env = options.env ?? process.env;
|
|
20834
|
+
const candidate = resolveNewJobProviderCandidate(store, env);
|
|
20835
|
+
if (!candidate) {
|
|
20836
|
+
return null;
|
|
20837
|
+
}
|
|
20838
|
+
return await bindResolvedJobProviderIfReady({
|
|
20839
|
+
store,
|
|
20840
|
+
jobId,
|
|
20841
|
+
provider: candidate.provider,
|
|
20842
|
+
bindingSource: candidate.bindingSource,
|
|
20843
|
+
env,
|
|
20844
|
+
platform: options.platform,
|
|
20845
|
+
readiness: options.readiness
|
|
20846
|
+
});
|
|
20847
|
+
}
|
|
20848
|
+
async function persistCurrentRuntimeSelectionForNewJob(options) {
|
|
20849
|
+
const store = options.store ?? null;
|
|
20850
|
+
if (!store) {
|
|
20851
|
+
return runtimeSelectionResult({ reason: "no_provider", ok: false });
|
|
20852
|
+
}
|
|
20853
|
+
const env = options.env ?? process.env;
|
|
20854
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20855
|
+
const previousProvider = store.getDefaultAiProvider();
|
|
20856
|
+
if (jobId && store.getJobProviderBinding(jobId)) {
|
|
20857
|
+
return runtimeSelectionResult({ reason: "already_bound", ok: true, previousProvider });
|
|
20858
|
+
}
|
|
20859
|
+
const explicit = env.OKX_AGENT_TASK_AI_CLI ?? env.OKX_A2A_AI_PROVIDER;
|
|
20860
|
+
const explicitProvider = explicit ? normalizeAiProvider(explicit) : null;
|
|
20861
|
+
let candidate = null;
|
|
20862
|
+
let bindingSource = null;
|
|
20863
|
+
if (explicitProvider) {
|
|
20864
|
+
candidate = explicitProvider;
|
|
20865
|
+
bindingSource = "explicit_env";
|
|
20866
|
+
} else {
|
|
20867
|
+
const runtime = detectRuntime(env, {
|
|
20868
|
+
parentPid: options.parentPid,
|
|
20869
|
+
readParentProcessCommand: options.readParentProcessCommand
|
|
20870
|
+
});
|
|
20871
|
+
if (runtime !== "unknown") {
|
|
20872
|
+
candidate = runtime;
|
|
20873
|
+
bindingSource = "current_runtime";
|
|
20874
|
+
} else if (previousProvider === "codex") {
|
|
20875
|
+
candidate = "codex";
|
|
20876
|
+
bindingSource = "default_provider";
|
|
20877
|
+
}
|
|
20878
|
+
}
|
|
20879
|
+
if (!candidate) {
|
|
20880
|
+
return runtimeSelectionResult({ reason: "runtime_undetectable", ok: true, previousProvider });
|
|
20881
|
+
}
|
|
20882
|
+
if (candidate !== "codex") {
|
|
20883
|
+
const changed2 = persistDefaultProviderIfChanged(store, candidate);
|
|
20884
|
+
return runtimeSelectionResult({
|
|
20885
|
+
reason: changed2 ? "persisted" : "unchanged",
|
|
20886
|
+
ok: true,
|
|
20887
|
+
provider: candidate,
|
|
20888
|
+
previousProvider,
|
|
20889
|
+
changed: changed2,
|
|
20890
|
+
bindingSource
|
|
20891
|
+
});
|
|
20892
|
+
}
|
|
20893
|
+
const readiness = await ensureProviderReadyForBinding({
|
|
20894
|
+
provider: "codex",
|
|
20895
|
+
store,
|
|
20896
|
+
env,
|
|
20897
|
+
platform: options.platform,
|
|
20898
|
+
readiness: options.readiness,
|
|
20899
|
+
// A validated override must be persisted here: this is exactly the case
|
|
20900
|
+
// where it has to outlive the current process.
|
|
20901
|
+
persistOverrideCommand: true
|
|
20902
|
+
});
|
|
20903
|
+
if (!readiness.ready) {
|
|
20904
|
+
return runtimeSelectionResult({
|
|
20905
|
+
reason: "provider_not_ready",
|
|
20906
|
+
ok: false,
|
|
20907
|
+
previousProvider,
|
|
20908
|
+
bindingSource,
|
|
20909
|
+
readiness,
|
|
20910
|
+
userMessage: `Codex is not ready (${readiness.state}); the default AI provider was not changed. ${readiness.recoveryGuidance}`.trim()
|
|
20911
|
+
});
|
|
20912
|
+
}
|
|
20913
|
+
const commandDurable = readiness.commandPersisted || codexCommandResolvesWithoutOverride(
|
|
20914
|
+
env,
|
|
20915
|
+
store.getAiProviderCommand("codex"),
|
|
20916
|
+
options.platform ?? process.platform
|
|
20917
|
+
);
|
|
20918
|
+
if (!commandDurable) {
|
|
20919
|
+
return runtimeSelectionResult({
|
|
20920
|
+
reason: "command_not_durable",
|
|
20921
|
+
ok: false,
|
|
20922
|
+
previousProvider,
|
|
20923
|
+
bindingSource,
|
|
20924
|
+
readiness,
|
|
20925
|
+
userMessage: "Codex is ready in this shell but only through OKX_A2A_AI_CODEX_COMMAND, which the background daemon does not inherit. The default AI provider was not changed. Run `okx-a2a setup --json` to persist a Codex command the daemon can resolve."
|
|
20926
|
+
});
|
|
20927
|
+
}
|
|
20928
|
+
const changed = persistDefaultProviderIfChanged(store, "codex");
|
|
20929
|
+
return runtimeSelectionResult({
|
|
20930
|
+
reason: changed ? "persisted" : "unchanged",
|
|
20931
|
+
ok: true,
|
|
20932
|
+
provider: "codex",
|
|
20933
|
+
previousProvider,
|
|
20934
|
+
changed,
|
|
20935
|
+
bindingSource,
|
|
20936
|
+
readiness,
|
|
20937
|
+
commandDurable: true
|
|
20938
|
+
});
|
|
20939
|
+
}
|
|
20940
|
+
async function prepareAndBindCurrentRuntimeForNewJob(options) {
|
|
20941
|
+
const store = options.store ?? null;
|
|
20942
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20943
|
+
if (!store || !jobId) {
|
|
20944
|
+
return {
|
|
20945
|
+
ok: false,
|
|
20946
|
+
jobId: jobId ?? "",
|
|
20947
|
+
provider: null,
|
|
20948
|
+
currentProvider: null,
|
|
20949
|
+
created: false,
|
|
20950
|
+
degraded: false,
|
|
20951
|
+
bindingSource: null,
|
|
20952
|
+
readiness: null,
|
|
20953
|
+
reason: "missing_job_id"
|
|
20954
|
+
};
|
|
20955
|
+
}
|
|
20956
|
+
const existing = store.getJobProviderBinding(jobId);
|
|
20957
|
+
if (existing) {
|
|
20958
|
+
return alreadyBoundGateResult(store, jobId, existing.provider, options.env);
|
|
20959
|
+
}
|
|
20960
|
+
const selection = await persistCurrentRuntimeSelectionForNewJob({
|
|
20961
|
+
store,
|
|
20962
|
+
jobId,
|
|
20963
|
+
env: options.env,
|
|
20964
|
+
platform: options.platform,
|
|
20965
|
+
readiness: options.readiness,
|
|
20966
|
+
parentPid: options.parentPid,
|
|
20967
|
+
readParentProcessCommand: options.readParentProcessCommand
|
|
20968
|
+
});
|
|
20969
|
+
if (selection.reason === "already_bound") {
|
|
20970
|
+
const raced = store.getJobProviderBinding(jobId);
|
|
20971
|
+
if (raced) {
|
|
20972
|
+
return alreadyBoundGateResult(store, jobId, raced.provider, options.env);
|
|
20973
|
+
}
|
|
20974
|
+
}
|
|
20975
|
+
if (!selection.ok) {
|
|
20976
|
+
return {
|
|
20977
|
+
ok: false,
|
|
20978
|
+
jobId,
|
|
20979
|
+
provider: null,
|
|
20980
|
+
currentProvider: selection.previousProvider,
|
|
20981
|
+
created: false,
|
|
20982
|
+
degraded: true,
|
|
20983
|
+
bindingSource: null,
|
|
20984
|
+
readiness: selection.readiness,
|
|
20985
|
+
reason: "provider_not_ready"
|
|
20986
|
+
};
|
|
20987
|
+
}
|
|
20988
|
+
if (!selection.provider) {
|
|
20989
|
+
return {
|
|
20990
|
+
ok: false,
|
|
20991
|
+
jobId,
|
|
20992
|
+
provider: null,
|
|
20993
|
+
currentProvider: selection.previousProvider,
|
|
20994
|
+
created: false,
|
|
20995
|
+
degraded: false,
|
|
20996
|
+
bindingSource: null,
|
|
20997
|
+
readiness: selection.readiness,
|
|
20998
|
+
reason: "unknown_runtime"
|
|
20999
|
+
};
|
|
21000
|
+
}
|
|
21001
|
+
return await bindResolvedJobProviderIfReady({
|
|
21002
|
+
store,
|
|
21003
|
+
jobId,
|
|
21004
|
+
provider: normalizeAiProvider(selection.provider),
|
|
21005
|
+
bindingSource: selection.bindingSource ?? "current_runtime",
|
|
21006
|
+
env: options.env,
|
|
21007
|
+
platform: options.platform,
|
|
21008
|
+
readiness: options.readiness,
|
|
21009
|
+
gate: selection.readiness
|
|
21010
|
+
});
|
|
21011
|
+
}
|
|
21012
|
+
function runtimeSelectionResult(input) {
|
|
21013
|
+
return {
|
|
21014
|
+
ok: input.ok,
|
|
21015
|
+
reason: input.reason,
|
|
21016
|
+
provider: input.provider ?? null,
|
|
21017
|
+
previousProvider: input.previousProvider ?? null,
|
|
21018
|
+
changed: input.changed ?? false,
|
|
21019
|
+
bindingSource: input.bindingSource ?? null,
|
|
21020
|
+
readiness: input.readiness ?? null,
|
|
21021
|
+
commandDurable: input.commandDurable ?? false,
|
|
21022
|
+
userMessage: input.userMessage ?? ""
|
|
21023
|
+
};
|
|
21024
|
+
}
|
|
21025
|
+
function notRequiredReadinessGate(provider) {
|
|
21026
|
+
return {
|
|
21027
|
+
provider,
|
|
21028
|
+
ready: true,
|
|
21029
|
+
state: "not_required",
|
|
21030
|
+
commandSource: null,
|
|
21031
|
+
commandPersisted: false,
|
|
21032
|
+
recoveryAction: "none",
|
|
21033
|
+
recoveryGuidance: ""
|
|
21034
|
+
};
|
|
21035
|
+
}
|
|
21036
|
+
function alreadyBoundGateResult(store, jobId, provider, env) {
|
|
21037
|
+
bindHermesJobRouteFromEnvIfAvailable(store, { jobId, provider, env });
|
|
21038
|
+
return {
|
|
21039
|
+
ok: true,
|
|
21040
|
+
jobId,
|
|
21041
|
+
provider,
|
|
21042
|
+
currentProvider: null,
|
|
21043
|
+
created: false,
|
|
21044
|
+
degraded: false,
|
|
21045
|
+
bindingSource: "existing_binding",
|
|
21046
|
+
readiness: null,
|
|
21047
|
+
reason: "already_bound"
|
|
21048
|
+
};
|
|
21049
|
+
}
|
|
21050
|
+
async function bindResolvedJobProviderIfReady(input) {
|
|
21051
|
+
const gate = input.gate ?? await ensureProviderReadyForBinding({
|
|
21052
|
+
provider: input.provider,
|
|
21053
|
+
store: input.store,
|
|
21054
|
+
env: input.env,
|
|
21055
|
+
platform: input.platform,
|
|
21056
|
+
readiness: input.readiness
|
|
21057
|
+
});
|
|
21058
|
+
logProviderReadinessChecked(input.jobId, gate);
|
|
21059
|
+
if (!gate.ready) {
|
|
21060
|
+
return {
|
|
21061
|
+
ok: false,
|
|
21062
|
+
jobId: input.jobId,
|
|
21063
|
+
provider: null,
|
|
21064
|
+
currentProvider: input.provider,
|
|
21065
|
+
created: false,
|
|
21066
|
+
degraded: true,
|
|
21067
|
+
bindingSource: null,
|
|
21068
|
+
readiness: gate,
|
|
21069
|
+
reason: "provider_not_ready"
|
|
21070
|
+
};
|
|
21071
|
+
}
|
|
21072
|
+
const result = bindJobProviderWithRouteIfAvailable(input.store, {
|
|
21073
|
+
jobId: input.jobId,
|
|
21074
|
+
provider: input.provider,
|
|
21075
|
+
env: input.env,
|
|
21076
|
+
// The gate above just returned ready for this exact provider.
|
|
21077
|
+
readinessVerified: true
|
|
21078
|
+
});
|
|
21079
|
+
const created = result.created;
|
|
21080
|
+
if (created) {
|
|
21081
|
+
logJobProviderBound(input.jobId, result.binding.provider, input.bindingSource, gate);
|
|
21082
|
+
}
|
|
21083
|
+
return {
|
|
21084
|
+
ok: true,
|
|
21085
|
+
jobId: input.jobId,
|
|
21086
|
+
provider: result.binding.provider,
|
|
21087
|
+
currentProvider: input.provider,
|
|
21088
|
+
created,
|
|
21089
|
+
degraded: false,
|
|
21090
|
+
// A concurrent writer won the INSERT: report the row as pre-existing rather
|
|
21091
|
+
// than claiming this call's candidate tier produced it.
|
|
21092
|
+
bindingSource: created ? input.bindingSource : "existing_binding",
|
|
21093
|
+
readiness: gate,
|
|
21094
|
+
reason: created ? "created" : "already_bound"
|
|
21095
|
+
};
|
|
21096
|
+
}
|
|
21097
|
+
function logProviderReadinessChecked(jobId, gate) {
|
|
21098
|
+
if (gate.state === "not_required") {
|
|
21099
|
+
return;
|
|
21100
|
+
}
|
|
21101
|
+
logger.info(LogEvent.PROVIDER_READINESS_CHECKED, {
|
|
21102
|
+
component: JOB_PROVIDER_BINDING_COMPONENT,
|
|
21103
|
+
jobId,
|
|
21104
|
+
provider: gate.provider,
|
|
21105
|
+
readinessState: gate.state,
|
|
21106
|
+
commandSource: gate.commandSource ?? "",
|
|
21107
|
+
commandPersisted: String(gate.commandPersisted),
|
|
21108
|
+
recoveryAction: gate.recoveryAction,
|
|
21109
|
+
...logFieldExtras({
|
|
21110
|
+
checkpoint: gate.ready ? "provider_ready" : "provider_not_ready",
|
|
21111
|
+
outcome: gate.ready ? "success" : "failed",
|
|
21112
|
+
transport: "local",
|
|
21113
|
+
eventFamily: "dispatch"
|
|
21114
|
+
})
|
|
21115
|
+
});
|
|
21116
|
+
}
|
|
21117
|
+
function logJobProviderBound(jobId, provider, bindingSource, gate) {
|
|
21118
|
+
logger.info(LogEvent.JOB_PROVIDER_BOUND, {
|
|
21119
|
+
component: JOB_PROVIDER_BINDING_COMPONENT,
|
|
21120
|
+
jobId,
|
|
21121
|
+
provider,
|
|
21122
|
+
bindingSource,
|
|
21123
|
+
readinessState: gate.state,
|
|
21124
|
+
commandSource: gate.commandSource ?? "",
|
|
21125
|
+
...logFieldExtras({
|
|
21126
|
+
checkpoint: "job_provider_bound",
|
|
21127
|
+
outcome: "success",
|
|
21128
|
+
transport: "local",
|
|
21129
|
+
eventFamily: "dispatch"
|
|
21130
|
+
})
|
|
21131
|
+
});
|
|
21132
|
+
}
|
|
21133
|
+
function resolveAiProviderForDispatch(options) {
|
|
21134
|
+
const env = options.env ?? process.env;
|
|
21135
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
21136
|
+
return resolveDispatchProvider(
|
|
21137
|
+
options,
|
|
21138
|
+
(provider) => bindDispatchProviderIfNeeded(options.store, jobId, env, provider)
|
|
21139
|
+
);
|
|
21140
|
+
}
|
|
21141
|
+
async function resolveAiProviderForDispatchWithReadiness(options) {
|
|
21142
|
+
const env = options.env ?? process.env;
|
|
21143
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
21144
|
+
if (!jobId || options.store.getJobProviderBinding(jobId)) {
|
|
21145
|
+
return resolveAiProviderForDispatch(options);
|
|
21146
|
+
}
|
|
21147
|
+
const candidate = resolveDispatchProvider(options, (provider) => provider);
|
|
21148
|
+
if (candidate !== "codex") {
|
|
21149
|
+
return resolveAiProviderForDispatch(options);
|
|
21150
|
+
}
|
|
21151
|
+
let gate = options.readiness ? await ensureProviderReadyForBinding({
|
|
21152
|
+
provider: candidate,
|
|
21153
|
+
store: options.store,
|
|
21154
|
+
env,
|
|
21155
|
+
platform: options.platform,
|
|
21156
|
+
readiness: options.readiness
|
|
21157
|
+
}) : cachedProviderReadinessGate(candidate, env);
|
|
21158
|
+
if (!gate) {
|
|
21159
|
+
gate = await ensureProviderReadyForBinding({
|
|
21160
|
+
provider: candidate,
|
|
21161
|
+
store: options.store,
|
|
21162
|
+
env,
|
|
21163
|
+
platform: options.platform
|
|
21164
|
+
});
|
|
21165
|
+
}
|
|
21166
|
+
logProviderReadinessChecked(jobId, gate);
|
|
21167
|
+
if (!gate.ready) {
|
|
21168
|
+
throw new ProviderNotReadyError(candidate, gate);
|
|
21169
|
+
}
|
|
21170
|
+
return resolveDispatchProvider(
|
|
21171
|
+
options,
|
|
21172
|
+
(provider) => bindDispatchProviderIfNeeded(options.store, jobId, env, provider, true)
|
|
21173
|
+
);
|
|
21174
|
+
}
|
|
21175
|
+
function cachedProviderReadinessGate(provider, env) {
|
|
21176
|
+
if (provider !== "codex") {
|
|
21177
|
+
return notRequiredReadinessGate(provider);
|
|
21178
|
+
}
|
|
21179
|
+
const cached = readCachedCodexReadiness({ env });
|
|
21180
|
+
return cached ? readinessGateFromResult(cached) : null;
|
|
21181
|
+
}
|
|
21182
|
+
function assertCodexBindingPermitted(store, input) {
|
|
21183
|
+
if (input.provider !== "codex" || input.readinessVerified) {
|
|
21184
|
+
return;
|
|
21185
|
+
}
|
|
21186
|
+
if (store.getJobProviderBinding?.(input.jobId)) {
|
|
21187
|
+
return;
|
|
21188
|
+
}
|
|
21189
|
+
const cached = readCachedCodexReadiness({ env: input.env ?? process.env });
|
|
21190
|
+
if (cached?.ready) {
|
|
21191
|
+
return;
|
|
21192
|
+
}
|
|
21193
|
+
throw new ProviderNotReadyError(
|
|
21194
|
+
input.provider,
|
|
21195
|
+
cached ? readinessGateFromResult(cached) : unverifiedCodexReadinessGate()
|
|
21196
|
+
);
|
|
21197
|
+
}
|
|
21198
|
+
function unverifiedCodexReadinessGate() {
|
|
21199
|
+
return {
|
|
21200
|
+
provider: "codex",
|
|
21201
|
+
ready: false,
|
|
21202
|
+
state: "probe_failed",
|
|
21203
|
+
commandSource: null,
|
|
21204
|
+
commandPersisted: false,
|
|
21205
|
+
recoveryAction: "run_setup",
|
|
21206
|
+
recoveryGuidance: "Codex readiness has not been verified for this job, so the binding was refused rather than pinned to a provider that may not be able to run. Run `okx-a2a setup --json` to verify Codex."
|
|
21207
|
+
};
|
|
21208
|
+
}
|
|
21209
|
+
function readinessGateFromResult(result) {
|
|
21210
|
+
return {
|
|
21211
|
+
provider: result.provider,
|
|
21212
|
+
ready: result.ready,
|
|
21213
|
+
state: result.state,
|
|
21214
|
+
commandSource: result.commandSource,
|
|
21215
|
+
commandPersisted: result.commandPersisted,
|
|
21216
|
+
recoveryAction: result.recoveryAction,
|
|
21217
|
+
recoveryGuidance: result.recoveryGuidance
|
|
21218
|
+
};
|
|
21219
|
+
}
|
|
21220
|
+
function resolveDispatchProvider(options, bind) {
|
|
21221
|
+
const env = options.env ?? process.env;
|
|
21222
|
+
const commandExists2 = options.commandExists ?? commandExists;
|
|
21223
|
+
const detection = withStoredCodexAvailability(
|
|
21224
|
+
detectAiProviders(commandExists2, env),
|
|
21225
|
+
options.store,
|
|
21226
|
+
commandExists2,
|
|
21227
|
+
env
|
|
21228
|
+
);
|
|
21229
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
21230
|
+
if (jobId) {
|
|
21231
|
+
const binding = options.store.getJobProviderBinding(jobId);
|
|
21232
|
+
if (binding) {
|
|
21233
|
+
assertInstalled(binding.provider, detection);
|
|
21234
|
+
return binding.provider;
|
|
21235
|
+
}
|
|
21236
|
+
}
|
|
21237
|
+
if (env.OKX_AGENT_TASK_AI_CLI) {
|
|
21238
|
+
const provider = normalizeAiProvider(env.OKX_AGENT_TASK_AI_CLI);
|
|
21239
|
+
assertInstalled(provider, detection);
|
|
21240
|
+
return bind(provider);
|
|
21241
|
+
}
|
|
21242
|
+
if (env.OKX_A2A_AI_PROVIDER) {
|
|
21243
|
+
const provider = normalizeAiProvider(env.OKX_A2A_AI_PROVIDER);
|
|
21244
|
+
assertInstalled(provider, detection);
|
|
21245
|
+
return bind(provider);
|
|
21246
|
+
}
|
|
21247
|
+
if (jobId) {
|
|
21248
|
+
const stored2 = options.store.getDefaultAiProvider();
|
|
21249
|
+
if (stored2) {
|
|
21250
|
+
assertInstalled(stored2, detection);
|
|
21251
|
+
return bind(stored2);
|
|
21252
|
+
}
|
|
21253
|
+
}
|
|
21254
|
+
const current = detectCurrentAiProvider(env);
|
|
21255
|
+
if (current && isInstalled(current, detection)) {
|
|
21256
|
+
return bind(current);
|
|
21257
|
+
}
|
|
21258
|
+
if (options.sessionKey) {
|
|
21259
|
+
const existing = options.store.listAiSessions(options.sessionKey).map((session) => session.provider).filter((provider) => isInstalled(provider, detection));
|
|
21260
|
+
const unique = [...new Set(existing)];
|
|
21261
|
+
if (unique.length === 1) {
|
|
21262
|
+
return bind(unique[0]);
|
|
21263
|
+
}
|
|
21264
|
+
}
|
|
21265
|
+
const stored = options.store.getDefaultAiProvider();
|
|
21266
|
+
if (stored) {
|
|
21267
|
+
assertInstalled(stored, detection);
|
|
21268
|
+
return bind(stored);
|
|
21269
|
+
}
|
|
21270
|
+
if (detection.available.length === 1) {
|
|
21271
|
+
return bind(detection.available[0]);
|
|
21272
|
+
}
|
|
21273
|
+
if (detection.available.length > 1) {
|
|
21274
|
+
throw new Error(
|
|
21275
|
+
`Multiple AI CLIs are installed (${detection.available.join(", ")}) but no current AI environment was detected. Set OKX_A2A_AI_PROVIDER or pass --provider/--ai-provider for this run.`
|
|
21276
|
+
);
|
|
21277
|
+
}
|
|
21278
|
+
throw new Error(`No supported AI CLI found. Install one of: ${AI_PROVIDERS.join(", ")}.`);
|
|
21279
|
+
}
|
|
21280
|
+
function bindDispatchProviderIfNeeded(store, jobId, env, provider, readinessVerified = false) {
|
|
21281
|
+
if (!jobId) {
|
|
21282
|
+
return provider;
|
|
21283
|
+
}
|
|
21284
|
+
return bindJobProviderWithRouteIfAvailable(store, { jobId, provider, env, readinessVerified }).binding.provider;
|
|
21285
|
+
}
|
|
21286
|
+
function withStoredCodexAvailability(detection, store, commandExists2, env = process.env, platform = process.platform) {
|
|
21287
|
+
if (detection.codex) {
|
|
21288
|
+
return detection;
|
|
21289
|
+
}
|
|
21290
|
+
const command = store.getAiProviderCommand("codex");
|
|
21291
|
+
if (command && commandExists2(command)) {
|
|
21292
|
+
return withCodexAvailable(detection);
|
|
21293
|
+
}
|
|
21294
|
+
for (const candidate of collectCodexAppBundledCandidates(env, platform)) {
|
|
21295
|
+
if (commandExists2(candidate)) {
|
|
21296
|
+
return withCodexAvailable(detection);
|
|
21297
|
+
}
|
|
21298
|
+
}
|
|
21299
|
+
return detection;
|
|
21300
|
+
}
|
|
21301
|
+
function withCodexAvailable(detection) {
|
|
21302
|
+
return {
|
|
21303
|
+
...detection,
|
|
21304
|
+
codex: true,
|
|
21305
|
+
available: detection.available.includes("codex") ? detection.available : ["codex", ...detection.available]
|
|
21306
|
+
};
|
|
21307
|
+
}
|
|
21308
|
+
function bindJobProviderWithRouteIfAvailable(store, input) {
|
|
21309
|
+
assertCodexBindingPermitted(store, input);
|
|
21310
|
+
const result = store.bindJobProviderIfMissing({
|
|
21311
|
+
jobId: input.jobId,
|
|
21312
|
+
provider: input.provider
|
|
21313
|
+
});
|
|
21314
|
+
const routed = bindHermesJobRouteFromEnvIfAvailable(store, {
|
|
21315
|
+
jobId: input.jobId,
|
|
21316
|
+
provider: result.binding.provider,
|
|
21317
|
+
env: input.env
|
|
21318
|
+
});
|
|
21319
|
+
return routed ? { ...result, binding: routed } : result;
|
|
21320
|
+
}
|
|
21321
|
+
function bindHermesJobRouteFromEnvIfAvailable(store, input) {
|
|
21322
|
+
if (input.provider !== "hermes" || typeof store.upsertJobGatewayRoute !== "function") {
|
|
21323
|
+
return null;
|
|
21324
|
+
}
|
|
21325
|
+
const route = currentHermesGatewayRouteFromEnv(input.env ?? process.env);
|
|
21326
|
+
if (!route) {
|
|
21327
|
+
return null;
|
|
21328
|
+
}
|
|
21329
|
+
return store.upsertJobGatewayRoute({
|
|
21330
|
+
jobId: input.jobId,
|
|
21331
|
+
provider: "hermes",
|
|
21332
|
+
route
|
|
21333
|
+
});
|
|
21334
|
+
}
|
|
21335
|
+
function currentHermesGatewayRouteFromEnv(env) {
|
|
21336
|
+
const platform = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_PLATFORM);
|
|
21337
|
+
const chatId = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_CHAT_ID);
|
|
21338
|
+
const envGatewaySessionKey = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_SESSION_KEY);
|
|
21339
|
+
const parsedGatewaySession = parseHermesGatewaySessionKey(envGatewaySessionKey);
|
|
21340
|
+
if (platform && chatId) {
|
|
21341
|
+
const threadId = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_THREAD_ID) ?? matchingParsedThreadId(parsedGatewaySession, { platform, chatId });
|
|
21342
|
+
return buildHermesGatewayRoute({
|
|
21343
|
+
platform,
|
|
21344
|
+
chatId,
|
|
21345
|
+
threadId,
|
|
21346
|
+
sessionKey: envGatewaySessionKey
|
|
21347
|
+
});
|
|
21348
|
+
}
|
|
21349
|
+
const sessionRoute = routeFromHermesSessionKey(env.HERMES_SESSION_KEY);
|
|
21350
|
+
return sessionRoute ? buildHermesGatewayRoute(sessionRoute) : null;
|
|
21351
|
+
}
|
|
21352
|
+
function buildHermesGatewayRoute(input) {
|
|
21353
|
+
const threadId = normalizeOptionalText(input.threadId) ?? "";
|
|
21354
|
+
const sessionKey = normalizeOptionalText(input.sessionKey) ?? (threadId ? hermesThreadSessionKey(input.platform, input.chatId, threadId) : `agent:main:${input.platform}:dm:${encodeURIComponent(input.chatId)}`);
|
|
21355
|
+
return {
|
|
21356
|
+
platform: input.platform,
|
|
21357
|
+
chatId: input.chatId,
|
|
21358
|
+
chatName: "",
|
|
21359
|
+
chatType: threadId ? "thread" : "dm",
|
|
21360
|
+
threadId,
|
|
21361
|
+
userId: "",
|
|
21362
|
+
userName: "",
|
|
21363
|
+
sessionKey,
|
|
21364
|
+
gatewaySessionKey: sessionKey
|
|
21365
|
+
};
|
|
21366
|
+
}
|
|
21367
|
+
function hermesThreadSessionKey(platform, chatId, threadId) {
|
|
21368
|
+
const encodedChatId = encodeURIComponent(chatId);
|
|
21369
|
+
const encodedThreadId = encodeURIComponent(threadId);
|
|
21370
|
+
return platform === "telegram" ? `agent:main:${platform}:dm:${encodedChatId}:${encodedThreadId}` : `agent:main:${platform}:thread:${encodedChatId}:${encodedThreadId}`;
|
|
21371
|
+
}
|
|
21372
|
+
function routeFromHermesSessionKey(value) {
|
|
21373
|
+
const sessionKey = normalizeOptionalText(value);
|
|
21374
|
+
if (!sessionKey) {
|
|
21375
|
+
return null;
|
|
21376
|
+
}
|
|
21377
|
+
const parsed = parseHermesGatewaySessionKey(sessionKey);
|
|
21378
|
+
return parsed ? { ...parsed, sessionKey } : null;
|
|
21379
|
+
}
|
|
21380
|
+
function parseHermesGatewaySessionKey(sessionKey) {
|
|
21381
|
+
const normalized = normalizeOptionalText(sessionKey);
|
|
21382
|
+
if (!normalized) {
|
|
21383
|
+
return null;
|
|
21384
|
+
}
|
|
21385
|
+
const parts = normalized.startsWith("agent:") ? normalized.split(":").slice(2) : normalized.split(":");
|
|
21386
|
+
if (parts.length < 3) {
|
|
21387
|
+
return null;
|
|
21388
|
+
}
|
|
21389
|
+
const [platform, scope, ...rest] = parts;
|
|
21390
|
+
if (!platform || platform === "okx-a2a" || platform === "backup" || platform === "job") {
|
|
21391
|
+
return null;
|
|
21392
|
+
}
|
|
21393
|
+
if (scope === "dm" && rest[0]) {
|
|
21394
|
+
return {
|
|
21395
|
+
platform,
|
|
21396
|
+
chatId: safeDecodeURIComponent2(rest[0]),
|
|
21397
|
+
...platform === "telegram" && rest[1] ? { threadId: safeDecodeURIComponent2(rest.slice(1).join(":")) } : {}
|
|
21398
|
+
};
|
|
21399
|
+
}
|
|
21400
|
+
if (scope === "thread" && rest[0] && rest[1]) {
|
|
21401
|
+
return {
|
|
21402
|
+
platform,
|
|
21403
|
+
chatId: safeDecodeURIComponent2(rest[0]),
|
|
21404
|
+
threadId: safeDecodeURIComponent2(rest.slice(1).join(":"))
|
|
21405
|
+
};
|
|
21406
|
+
}
|
|
21407
|
+
return null;
|
|
21408
|
+
}
|
|
21409
|
+
function matchingParsedThreadId(parsed, expected) {
|
|
21410
|
+
if (!parsed?.threadId) {
|
|
21411
|
+
return null;
|
|
21412
|
+
}
|
|
21413
|
+
return parsed.platform === expected.platform && parsed.chatId === expected.chatId ? parsed.threadId : null;
|
|
21414
|
+
}
|
|
21415
|
+
function safeDecodeURIComponent2(value) {
|
|
21416
|
+
try {
|
|
21417
|
+
return decodeURIComponent(value);
|
|
21418
|
+
} catch {
|
|
21419
|
+
return value;
|
|
21420
|
+
}
|
|
21421
|
+
}
|
|
21422
|
+
function normalizeOptionalJobId2(value) {
|
|
21423
|
+
const normalized = value?.trim();
|
|
21424
|
+
return normalized ? normalized : null;
|
|
21425
|
+
}
|
|
21426
|
+
function normalizeOptionalText(value) {
|
|
21427
|
+
const normalized = value?.trim();
|
|
21428
|
+
return normalized ? normalized : null;
|
|
21429
|
+
}
|
|
21430
|
+
function isInstalled(provider, detection) {
|
|
21431
|
+
return detection.available.includes(provider);
|
|
21432
|
+
}
|
|
21433
|
+
function assertInstalled(provider, detection) {
|
|
21434
|
+
if (!isInstalled(provider, detection)) {
|
|
21435
|
+
throw new Error(`AI provider "${provider}" is not installed or not on PATH.`);
|
|
21436
|
+
}
|
|
21437
|
+
}
|
|
21438
|
+
function readProviderCommand(provider, env) {
|
|
21439
|
+
return env[`OKX_A2A_AI_${provider.toUpperCase()}_COMMAND`] ?? provider;
|
|
21440
|
+
}
|
|
21441
|
+
async function promptForAiProvider(options) {
|
|
21442
|
+
const stdin = options.stdin ?? process.stdin;
|
|
21443
|
+
const stdout = options.stdout ?? process.stdout;
|
|
21444
|
+
if (!stdin.isTTY || !stdout.isTTY) {
|
|
21445
|
+
throw new Error(
|
|
21446
|
+
`No AI provider is configured. Run \`okx-a2a daemon start --ai-provider <${AI_PROVIDERS.join("|")}>\` or set OKX_A2A_AI_PROVIDER.`
|
|
21447
|
+
);
|
|
21448
|
+
}
|
|
21449
|
+
stdout.write("AI provider is required before starting okx-a2a.\n");
|
|
21450
|
+
stdout.write("Tip: if the selected AI provider is unavailable, task processing cannot proceed.\n\n");
|
|
21451
|
+
if (options.currentProvider) {
|
|
21452
|
+
stdout.write(`Current AI provider: ${options.currentProvider}
|
|
21453
|
+
|
|
21454
|
+
`);
|
|
21455
|
+
}
|
|
21456
|
+
if (options.detection.available.length === 0) {
|
|
21457
|
+
stdout.write("Supported AI providers:\n");
|
|
21458
|
+
AI_PROVIDERS.forEach((provider, index2) => {
|
|
21459
|
+
const status = isInstalled(provider, options.detection) ? "installed" : "not found on PATH";
|
|
21460
|
+
stdout.write(` ${index2 + 1}. ${provider} (${status})
|
|
21461
|
+
`);
|
|
21462
|
+
});
|
|
21463
|
+
stdout.write("\n");
|
|
21464
|
+
throw new Error(`No supported AI CLI found. Install one of: ${AI_PROVIDERS.join(", ")}.`);
|
|
21465
|
+
}
|
|
21466
|
+
if (typeof stdin.setRawMode === "function") {
|
|
21467
|
+
return promptForAiProviderWithArrows({
|
|
21468
|
+
detection: options.detection,
|
|
21469
|
+
stdin,
|
|
21470
|
+
stdout,
|
|
21471
|
+
currentProvider: options.currentProvider
|
|
21472
|
+
});
|
|
21473
|
+
}
|
|
21474
|
+
stdout.write("Supported AI providers:\n");
|
|
21475
|
+
AI_PROVIDERS.forEach((provider, index2) => {
|
|
21476
|
+
const status = isInstalled(provider, options.detection) ? "installed" : "not found on PATH";
|
|
21477
|
+
stdout.write(` ${index2 + 1}. ${provider} (${status})
|
|
21478
|
+
`);
|
|
21479
|
+
});
|
|
21480
|
+
stdout.write("\n");
|
|
21481
|
+
const rl = (0, import_promises6.createInterface)({ input: stdin, output: stdout });
|
|
21482
|
+
try {
|
|
21483
|
+
while (true) {
|
|
21484
|
+
const answer = (await rl.question("Choose an installed AI provider by number or name: ")).trim();
|
|
21485
|
+
const provider = parseProviderChoice(answer);
|
|
21486
|
+
if (!provider) {
|
|
21487
|
+
stdout.write(`Invalid choice. Use one of: ${AI_PROVIDERS.join(", ")}.
|
|
21488
|
+
`);
|
|
21489
|
+
continue;
|
|
21490
|
+
}
|
|
21491
|
+
if (!isInstalled(provider, options.detection)) {
|
|
21492
|
+
stdout.write(`"${provider}" is not installed or not on PATH. Choose an installed provider.
|
|
21493
|
+
`);
|
|
21494
|
+
continue;
|
|
20576
21495
|
}
|
|
20577
|
-
|
|
20578
|
-
|
|
20579
|
-
|
|
20580
|
-
|
|
20581
|
-
|
|
20582
|
-
|
|
20583
|
-
|
|
20584
|
-
|
|
20585
|
-
|
|
21496
|
+
return provider;
|
|
21497
|
+
}
|
|
21498
|
+
} finally {
|
|
21499
|
+
rl.close();
|
|
21500
|
+
}
|
|
21501
|
+
}
|
|
21502
|
+
async function promptForAiProviderWithArrows(options) {
|
|
21503
|
+
let selectedIndex = AI_PROVIDERS.findIndex((provider) => provider === options.currentProvider && isInstalled(provider, options.detection));
|
|
21504
|
+
if (selectedIndex < 0) {
|
|
21505
|
+
selectedIndex = AI_PROVIDERS.findIndex((provider) => isInstalled(provider, options.detection));
|
|
21506
|
+
}
|
|
21507
|
+
if (selectedIndex < 0) {
|
|
21508
|
+
selectedIndex = 0;
|
|
21509
|
+
}
|
|
21510
|
+
let renderedLines = 0;
|
|
21511
|
+
const render = (message) => {
|
|
21512
|
+
if (renderedLines > 0) {
|
|
21513
|
+
(0, import_node_readline.moveCursor)(options.stdout, 0, -renderedLines);
|
|
21514
|
+
(0, import_node_readline.cursorTo)(options.stdout, 0);
|
|
21515
|
+
(0, import_node_readline.clearScreenDown)(options.stdout);
|
|
21516
|
+
}
|
|
21517
|
+
const lines = [
|
|
21518
|
+
"Use \u2191/\u2193 to move, Enter to confirm.",
|
|
21519
|
+
"",
|
|
21520
|
+
...AI_PROVIDERS.map((provider, index2) => {
|
|
21521
|
+
const selected = index2 === selectedIndex ? "\u276F" : " ";
|
|
21522
|
+
const status = isInstalled(provider, options.detection) ? "installed" : "not found on PATH";
|
|
21523
|
+
const current = provider === options.currentProvider ? ", current" : "";
|
|
21524
|
+
return `${selected} ${provider} (${status}${current})`;
|
|
21525
|
+
}),
|
|
21526
|
+
...message ? ["", message] : []
|
|
21527
|
+
];
|
|
21528
|
+
options.stdout.write(`${lines.join("\n")}
|
|
21529
|
+
`);
|
|
21530
|
+
renderedLines = lines.length;
|
|
21531
|
+
};
|
|
21532
|
+
(0, import_node_readline.emitKeypressEvents)(options.stdin);
|
|
21533
|
+
options.stdin.setRawMode(true);
|
|
21534
|
+
options.stdin.resume();
|
|
21535
|
+
options.stdout.write("\x1B[?25l");
|
|
21536
|
+
render();
|
|
21537
|
+
return await new Promise((resolve11, reject) => {
|
|
21538
|
+
const cleanup = () => {
|
|
21539
|
+
options.stdin.off("keypress", onKeypress);
|
|
21540
|
+
options.stdin.setRawMode(false);
|
|
21541
|
+
options.stdin.pause();
|
|
21542
|
+
options.stdout.write("\x1B[?25h");
|
|
21543
|
+
};
|
|
21544
|
+
const clearRendered = () => {
|
|
21545
|
+
if (renderedLines > 0) {
|
|
21546
|
+
(0, import_node_readline.moveCursor)(options.stdout, 0, -renderedLines);
|
|
21547
|
+
(0, import_node_readline.cursorTo)(options.stdout, 0);
|
|
21548
|
+
(0, import_node_readline.clearScreenDown)(options.stdout);
|
|
20586
21549
|
}
|
|
20587
|
-
|
|
20588
|
-
|
|
20589
|
-
|
|
21550
|
+
};
|
|
21551
|
+
const finish = (provider) => {
|
|
21552
|
+
cleanup();
|
|
21553
|
+
clearRendered();
|
|
21554
|
+
options.stdout.write(`Selected AI provider: ${provider}
|
|
21555
|
+
`);
|
|
21556
|
+
resolve11(provider);
|
|
21557
|
+
};
|
|
21558
|
+
const onKeypress = (_str, key) => {
|
|
21559
|
+
if (key.ctrl && key.name === "c") {
|
|
21560
|
+
cleanup();
|
|
21561
|
+
clearRendered();
|
|
21562
|
+
options.stdout.write(
|
|
21563
|
+
options.currentProvider ? `AI provider selection cancelled. Keeping current provider: ${options.currentProvider}
|
|
21564
|
+
` : "AI provider selection cancelled. No provider was configured.\n"
|
|
20590
21565
|
);
|
|
21566
|
+
reject(new UserCancelledAiProviderSelectionError());
|
|
21567
|
+
return;
|
|
20591
21568
|
}
|
|
20592
|
-
|
|
20593
|
-
|
|
20594
|
-
|
|
20595
|
-
|
|
20596
|
-
return true;
|
|
20597
|
-
}
|
|
20598
|
-
if (_SentryLogger.isSafeMetricKey(normalized)) {
|
|
20599
|
-
return false;
|
|
20600
|
-
}
|
|
20601
|
-
return SENTRY_EXTRA_BLOCKED_KEY_PARTS.some((needle) => normalized.includes(needle));
|
|
20602
|
-
}
|
|
20603
|
-
static isSafeMetricKey(normalized) {
|
|
20604
|
-
return /(bytes|count|length|ms|size)$/.test(normalized);
|
|
20605
|
-
}
|
|
20606
|
-
static safeValueForKey(key, value) {
|
|
20607
|
-
if (typeof value === "string" && SENTRY_FULL_STRING_EXTRA_KEYS.has(_SentryLogger.normalizeSensitiveKey(key))) {
|
|
20608
|
-
return value;
|
|
20609
|
-
}
|
|
20610
|
-
return _SentryLogger.safeValue(value);
|
|
20611
|
-
}
|
|
20612
|
-
static normalizeSensitiveKey(key) {
|
|
20613
|
-
return key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
20614
|
-
}
|
|
20615
|
-
static safeValue(value) {
|
|
20616
|
-
if (typeof value === "string") {
|
|
20617
|
-
return value.length <= MAX_EXTRA_STRING_LENGTH ? value : `${value.slice(0, MAX_EXTRA_STRING_LENGTH)}...`;
|
|
20618
|
-
}
|
|
20619
|
-
if (value === null || value === void 0 || typeof value === "number" || typeof value === "boolean") {
|
|
20620
|
-
return value;
|
|
20621
|
-
}
|
|
20622
|
-
if (typeof value === "bigint") {
|
|
20623
|
-
return value.toString();
|
|
20624
|
-
}
|
|
20625
|
-
if (value instanceof Error) {
|
|
20626
|
-
return {
|
|
20627
|
-
name: value.name,
|
|
20628
|
-
messageLength: value.message.length
|
|
20629
|
-
};
|
|
20630
|
-
}
|
|
20631
|
-
if (Array.isArray(value)) {
|
|
20632
|
-
return value.slice(0, 20).map((item) => _SentryLogger.safeValue(item));
|
|
20633
|
-
}
|
|
20634
|
-
if (typeof value === "object") {
|
|
20635
|
-
return Object.fromEntries(
|
|
20636
|
-
Object.entries(value).slice(0, 50).filter(([, item]) => item !== void 0).filter(([key]) => !_SentryLogger.isBlockedExtraKey(key)).map(([key, item]) => [key, _SentryLogger.safeValueForKey(key, item)])
|
|
20637
|
-
);
|
|
20638
|
-
}
|
|
20639
|
-
const text = String(value);
|
|
20640
|
-
return text.length <= MAX_EXTRA_STRING_LENGTH ? text : `${text.slice(0, MAX_EXTRA_STRING_LENGTH)}...`;
|
|
20641
|
-
}
|
|
20642
|
-
static tagValue(value) {
|
|
20643
|
-
const text = String(value);
|
|
20644
|
-
return text.length <= MAX_TAG_VALUE_LENGTH ? text : `${text.slice(0, MAX_TAG_VALUE_LENGTH)}...`;
|
|
21569
|
+
if (key.name === "up") {
|
|
21570
|
+
selectedIndex = (selectedIndex + AI_PROVIDERS.length - 1) % AI_PROVIDERS.length;
|
|
21571
|
+
render();
|
|
21572
|
+
return;
|
|
20645
21573
|
}
|
|
20646
|
-
|
|
20647
|
-
|
|
21574
|
+
if (key.name === "down") {
|
|
21575
|
+
selectedIndex = (selectedIndex + 1) % AI_PROVIDERS.length;
|
|
21576
|
+
render();
|
|
21577
|
+
return;
|
|
20648
21578
|
}
|
|
20649
|
-
|
|
20650
|
-
const
|
|
20651
|
-
|
|
20652
|
-
|
|
20653
|
-
|
|
20654
|
-
}
|
|
20655
|
-
scope.setTag(_SentryLogger.normalizeTagKey(key), _SentryLogger.tagValue(value));
|
|
20656
|
-
}
|
|
20657
|
-
const fingerprint = ["a2a", eventName];
|
|
20658
|
-
for (const key of SENTRY_FINGERPRINT_KEYS) {
|
|
20659
|
-
const value = enriched[key];
|
|
20660
|
-
if (value) {
|
|
20661
|
-
fingerprint.push(`${key}:${_SentryLogger.tagValue(value)}`);
|
|
20662
|
-
}
|
|
21579
|
+
if (key.name === "return" || key.name === "enter") {
|
|
21580
|
+
const provider = AI_PROVIDERS[selectedIndex];
|
|
21581
|
+
if (!isInstalled(provider, options.detection)) {
|
|
21582
|
+
render(`"${provider}" is not installed or not on PATH. Choose an installed provider.`);
|
|
21583
|
+
return;
|
|
20663
21584
|
}
|
|
20664
|
-
|
|
20665
|
-
}
|
|
20666
|
-
static toPascalCase(str) {
|
|
20667
|
-
return str.split(/[\s:]+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
20668
|
-
}
|
|
20669
|
-
static createSentryEvent(eventName) {
|
|
20670
|
-
const err = new Error(eventName);
|
|
20671
|
-
err.name = _SentryLogger.toPascalCase(eventName);
|
|
20672
|
-
err.stack = `${err.name}: ${eventName}`;
|
|
20673
|
-
return err;
|
|
21585
|
+
finish(provider);
|
|
20674
21586
|
}
|
|
20675
21587
|
};
|
|
20676
|
-
|
|
20677
|
-
|
|
20678
|
-
|
|
21588
|
+
options.stdin.on("keypress", onKeypress);
|
|
21589
|
+
});
|
|
21590
|
+
}
|
|
21591
|
+
function parseProviderChoice(answer) {
|
|
21592
|
+
if (!answer) {
|
|
21593
|
+
return null;
|
|
21594
|
+
}
|
|
21595
|
+
const number = Number(answer);
|
|
21596
|
+
if (Number.isInteger(number) && number >= 1 && number <= AI_PROVIDERS.length) {
|
|
21597
|
+
return AI_PROVIDERS[number - 1];
|
|
21598
|
+
}
|
|
21599
|
+
try {
|
|
21600
|
+
return normalizeAiProvider(answer);
|
|
21601
|
+
} catch {
|
|
21602
|
+
return null;
|
|
21603
|
+
}
|
|
21604
|
+
}
|
|
21605
|
+
var import_node_child_process5, import_promises6, import_node_readline, AI_PROVIDERS, UserCancelledAiProviderSelectionError, JOB_PROVIDER_BINDING_COMPONENT, ProviderNotReadyError;
|
|
21606
|
+
var init_ai_provider = __esm({
|
|
21607
|
+
"src/ai-provider.ts"() {
|
|
21608
|
+
"use strict";
|
|
21609
|
+
import_node_child_process5 = require("node:child_process");
|
|
21610
|
+
import_promises6 = require("node:readline/promises");
|
|
21611
|
+
import_node_readline = require("node:readline");
|
|
21612
|
+
init_ai_command();
|
|
21613
|
+
init_codex_readiness();
|
|
21614
|
+
init_sentry_logger();
|
|
21615
|
+
init_log_fields();
|
|
21616
|
+
init_win_spawn();
|
|
21617
|
+
AI_PROVIDERS = ["codex", "claude", "hermes", "openclaw"];
|
|
21618
|
+
UserCancelledAiProviderSelectionError = class extends Error {
|
|
21619
|
+
constructor() {
|
|
21620
|
+
super("AI provider selection cancelled by user.");
|
|
21621
|
+
this.name = "UserCancelledAiProviderSelectionError";
|
|
21622
|
+
}
|
|
20679
21623
|
};
|
|
20680
|
-
|
|
20681
|
-
|
|
21624
|
+
JOB_PROVIDER_BINDING_COMPONENT = "node_job_provider_binding";
|
|
21625
|
+
ProviderNotReadyError = class extends Error {
|
|
21626
|
+
provider;
|
|
21627
|
+
readinessState;
|
|
21628
|
+
recoveryAction;
|
|
21629
|
+
commandSource;
|
|
21630
|
+
constructor(provider, gate) {
|
|
21631
|
+
super(`AI provider "${provider}" is not ready (${gate.state}). ${gate.recoveryGuidance}`.trim());
|
|
21632
|
+
this.name = "ProviderNotReadyError";
|
|
21633
|
+
this.provider = provider;
|
|
21634
|
+
this.readinessState = gate.state;
|
|
21635
|
+
this.recoveryAction = gate.recoveryAction;
|
|
21636
|
+
this.commandSource = gate.commandSource;
|
|
21637
|
+
}
|
|
20682
21638
|
};
|
|
20683
|
-
UNKNOWN_FIELD = "unknown";
|
|
20684
21639
|
}
|
|
20685
21640
|
});
|
|
20686
21641
|
|
|
@@ -31149,7 +32104,7 @@ var init_sentry_config = __esm({
|
|
|
31149
32104
|
environment = process.env.SENTRY_ENV === "dev" ? "dev" : "prod";
|
|
31150
32105
|
SENTRY_CONFIG = {
|
|
31151
32106
|
projectName: "okx/openclaw-okx-a2a-extension",
|
|
31152
|
-
release: "0.2.
|
|
32107
|
+
release: "0.2.3-beta-6f2a8ec2c7-260810184309",
|
|
31153
32108
|
environment,
|
|
31154
32109
|
runtimeContainer: normalizeRuntimeContainer(process.env.OKX_A2A_RUNTIME_CONTAINER)
|
|
31155
32110
|
};
|
|
@@ -31362,6 +32317,7 @@ var init_win_native_launcher = __esm({
|
|
|
31362
32317
|
var update_cli_exports = {};
|
|
31363
32318
|
__export(update_cli_exports, {
|
|
31364
32319
|
assertHermesSetupSupportedOnPlatform: () => assertHermesSetupSupportedOnPlatform,
|
|
32320
|
+
authStatusFromReason: () => authStatusFromReason,
|
|
31365
32321
|
buildExternalCommandInvocation: () => buildExternalCommandInvocation,
|
|
31366
32322
|
buildNpmPackageSpec: () => buildNpmPackageSpec,
|
|
31367
32323
|
buildProviderMismatchWarning: () => buildProviderMismatchWarning,
|
|
@@ -31881,7 +32837,7 @@ function authStatusFromReason(reason) {
|
|
|
31881
32837
|
if (reason === "provider_cli_login_failed") {
|
|
31882
32838
|
return "login_failed";
|
|
31883
32839
|
}
|
|
31884
|
-
if (reason === "provider_cli_login_timeout") {
|
|
32840
|
+
if (reason === "provider_cli_login_timeout" || reason === "provider_cli_auth_status_timeout") {
|
|
31885
32841
|
return "login_timeout";
|
|
31886
32842
|
}
|
|
31887
32843
|
return "blocked";
|
|
@@ -31980,8 +32936,13 @@ function quoteUserFacingCommandPath(commandPath) {
|
|
|
31980
32936
|
}
|
|
31981
32937
|
return `"${commandPath}"`;
|
|
31982
32938
|
}
|
|
31983
|
-
async function checkClaudeCliAuthStatus(command) {
|
|
31984
|
-
const result = await runCommandCaptureStatus(command, ["auth", "status", "--json"]
|
|
32939
|
+
async function checkClaudeCliAuthStatus(command, options = {}) {
|
|
32940
|
+
const result = await runCommandCaptureStatus(command, ["auth", "status", "--json"], {
|
|
32941
|
+
timeoutMs: options.timeoutMs ?? PROVIDER_CLI_AUTH_PROBE_TIMEOUT_MS
|
|
32942
|
+
});
|
|
32943
|
+
if (result.errorCode === "ETIMEDOUT") {
|
|
32944
|
+
return { ok: false, reason: "provider_cli_auth_status_timeout", detail: result.detail };
|
|
32945
|
+
}
|
|
31985
32946
|
if (result.errorCode === "ENOENT") {
|
|
31986
32947
|
return { ok: false, reason: "provider_cli_missing", detail: result.detail };
|
|
31987
32948
|
}
|
|
@@ -32000,8 +32961,13 @@ async function checkClaudeCliAuthStatus(command) {
|
|
|
32000
32961
|
${result.stdout}`.trim() };
|
|
32001
32962
|
}
|
|
32002
32963
|
}
|
|
32003
|
-
async function checkCodexCliAuthStatus(command) {
|
|
32004
|
-
const result = await runCommandCaptureStatus(command, ["login", "status"]
|
|
32964
|
+
async function checkCodexCliAuthStatus(command, options = {}) {
|
|
32965
|
+
const result = await runCommandCaptureStatus(command, ["login", "status"], {
|
|
32966
|
+
timeoutMs: options.timeoutMs ?? PROVIDER_CLI_AUTH_PROBE_TIMEOUT_MS
|
|
32967
|
+
});
|
|
32968
|
+
if (result.errorCode === "ETIMEDOUT") {
|
|
32969
|
+
return { ok: false, reason: "provider_cli_auth_status_timeout", detail: result.detail };
|
|
32970
|
+
}
|
|
32005
32971
|
if (result.errorCode === "ENOENT") {
|
|
32006
32972
|
return { ok: false, reason: "provider_cli_missing", detail: result.detail };
|
|
32007
32973
|
}
|
|
@@ -32079,7 +33045,7 @@ async function getCurrentNodeCliVersion() {
|
|
|
32079
33045
|
return await getGlobalNpmPackageVersion(UPDATE_PACKAGES.node) ?? getBundledNodeCliVersion();
|
|
32080
33046
|
}
|
|
32081
33047
|
function getBundledNodeCliVersion() {
|
|
32082
|
-
return true ? "0.2.
|
|
33048
|
+
return true ? "0.2.3-beta-6f2a8ec2c7-260810184309" : null;
|
|
32083
33049
|
}
|
|
32084
33050
|
function readConfiguredAiProvider() {
|
|
32085
33051
|
const explicit = process.env.OKX_A2A_AI_PROVIDER || process.env.OKX_AGENT_TASK_AI_CLI;
|
|
@@ -32289,7 +33255,7 @@ async function updateHermes(release, options) {
|
|
|
32289
33255
|
}
|
|
32290
33256
|
}
|
|
32291
33257
|
async function installGatewayPluginForDoctor(target) {
|
|
32292
|
-
const release = isPrereleaseVersion("0.2.
|
|
33258
|
+
const release = isPrereleaseVersion("0.2.3-beta-6f2a8ec2c7-260810184309") ? "beta" : "latest";
|
|
32293
33259
|
const insideTargetGateway = detectGatewayInvocation() === target;
|
|
32294
33260
|
const options = {
|
|
32295
33261
|
restart: !insideTargetGateway,
|
|
@@ -32970,7 +33936,7 @@ async function runCommandCaptureOptional(command, args) {
|
|
|
32970
33936
|
});
|
|
32971
33937
|
});
|
|
32972
33938
|
}
|
|
32973
|
-
async function runCommandCaptureStatus(command, args) {
|
|
33939
|
+
async function runCommandCaptureStatus(command, args, options = {}) {
|
|
32974
33940
|
return await new Promise((resolvePromise) => {
|
|
32975
33941
|
const invocation = buildExternalCommandInvocation(command, args);
|
|
32976
33942
|
const child = (0, import_node_child_process8.spawn)(invocation.command, invocation.args, {
|
|
@@ -32979,6 +33945,18 @@ async function runCommandCaptureStatus(command, args) {
|
|
|
32979
33945
|
});
|
|
32980
33946
|
let stdout = "";
|
|
32981
33947
|
let stderr = "";
|
|
33948
|
+
let settled = false;
|
|
33949
|
+
let timeout = null;
|
|
33950
|
+
const settle = (result) => {
|
|
33951
|
+
if (settled) {
|
|
33952
|
+
return;
|
|
33953
|
+
}
|
|
33954
|
+
settled = true;
|
|
33955
|
+
if (timeout) {
|
|
33956
|
+
clearTimeout(timeout);
|
|
33957
|
+
}
|
|
33958
|
+
resolvePromise(result);
|
|
33959
|
+
};
|
|
32982
33960
|
child.stdout.setEncoding("utf8");
|
|
32983
33961
|
child.stderr.setEncoding("utf8");
|
|
32984
33962
|
child.stdout.on("data", (chunk) => {
|
|
@@ -32987,9 +33965,28 @@ async function runCommandCaptureStatus(command, args) {
|
|
|
32987
33965
|
child.stderr.on("data", (chunk) => {
|
|
32988
33966
|
stderr += chunk;
|
|
32989
33967
|
});
|
|
33968
|
+
if (options.timeoutMs && options.timeoutMs > 0) {
|
|
33969
|
+
const timeoutMs = options.timeoutMs;
|
|
33970
|
+
timeout = setTimeout(() => {
|
|
33971
|
+
killProcessTree(child, "SIGTERM");
|
|
33972
|
+
setTimeout(() => {
|
|
33973
|
+
if (!child.killed || child.exitCode === null) {
|
|
33974
|
+
killProcessTree(child, "SIGKILL");
|
|
33975
|
+
}
|
|
33976
|
+
}, 5e3).unref();
|
|
33977
|
+
settle({
|
|
33978
|
+
code: null,
|
|
33979
|
+
stdout,
|
|
33980
|
+
stderr,
|
|
33981
|
+
detail: `timed out after ${timeoutMs}ms`,
|
|
33982
|
+
errorCode: "ETIMEDOUT"
|
|
33983
|
+
});
|
|
33984
|
+
}, timeoutMs);
|
|
33985
|
+
timeout.unref();
|
|
33986
|
+
}
|
|
32990
33987
|
child.on("error", (error) => {
|
|
32991
33988
|
const detail = error.message;
|
|
32992
|
-
|
|
33989
|
+
settle({
|
|
32993
33990
|
code: null,
|
|
32994
33991
|
stdout,
|
|
32995
33992
|
stderr,
|
|
@@ -33000,7 +33997,7 @@ async function runCommandCaptureStatus(command, args) {
|
|
|
33000
33997
|
child.on("close", (code, signal) => {
|
|
33001
33998
|
const signalDetail = signal ? `signal=${signal}` : "";
|
|
33002
33999
|
const detail = [stderr.trim(), stdout.trim(), signalDetail].filter(Boolean).join("\n");
|
|
33003
|
-
|
|
34000
|
+
settle({
|
|
33004
34001
|
code,
|
|
33005
34002
|
stdout,
|
|
33006
34003
|
stderr,
|
|
@@ -33015,7 +34012,7 @@ function buildExternalCommandInvocation(command, args, platform = process.platfo
|
|
|
33015
34012
|
function formatExternalCommand(command, args) {
|
|
33016
34013
|
return [command, ...args].join(" ");
|
|
33017
34014
|
}
|
|
33018
|
-
var import_node_child_process8, import_node_fs22, import_promises11, import_node_os11, import_node_path28, UPDATE_PACKAGES, CODEX_NPM_PACKAGE, OPENCLAW_FORCE_INSTALL_FLAG, OPENCLAW_UNSAFE_INSTALL_FLAG, OKX_A2A_PLUGIN_ID, OPENCLAW_SESSION_DM_SCOPE_CONFIG_PATH, OPENCLAW_SESSION_DM_SCOPE_CONFIG_VALUE, OPENCLAW_PLUGIN_ALLOW_CONFIG_PATH, OPENCLAW_OKX_A2A_CONVERSATION_HOOK_ACCESS_CONFIG_PATH, DEFAULT_AI_PROVIDER_LOGIN_TIMEOUT_MS, SetupBlockedError, redirectCommandStdoutToStderr;
|
|
34015
|
+
var import_node_child_process8, import_node_fs22, import_promises11, import_node_os11, import_node_path28, UPDATE_PACKAGES, CODEX_NPM_PACKAGE, OPENCLAW_FORCE_INSTALL_FLAG, OPENCLAW_UNSAFE_INSTALL_FLAG, OKX_A2A_PLUGIN_ID, OPENCLAW_SESSION_DM_SCOPE_CONFIG_PATH, OPENCLAW_SESSION_DM_SCOPE_CONFIG_VALUE, OPENCLAW_PLUGIN_ALLOW_CONFIG_PATH, OPENCLAW_OKX_A2A_CONVERSATION_HOOK_ACCESS_CONFIG_PATH, DEFAULT_AI_PROVIDER_LOGIN_TIMEOUT_MS, PROVIDER_CLI_AUTH_PROBE_TIMEOUT_MS, SetupBlockedError, redirectCommandStdoutToStderr;
|
|
33019
34016
|
var init_update_cli = __esm({
|
|
33020
34017
|
"src/update-cli.ts"() {
|
|
33021
34018
|
"use strict";
|
|
@@ -33042,6 +34039,7 @@ var init_update_cli = __esm({
|
|
|
33042
34039
|
OPENCLAW_PLUGIN_ALLOW_CONFIG_PATH = "plugins.allow";
|
|
33043
34040
|
OPENCLAW_OKX_A2A_CONVERSATION_HOOK_ACCESS_CONFIG_PATH = "plugins.entries.okx-a2a.hooks.allowConversationAccess";
|
|
33044
34041
|
DEFAULT_AI_PROVIDER_LOGIN_TIMEOUT_MS = 3 * 60 * 1e3;
|
|
34042
|
+
PROVIDER_CLI_AUTH_PROBE_TIMEOUT_MS = CODEX_PROBE_TIMEOUT_MS;
|
|
33045
34043
|
SetupBlockedError = class extends Error {
|
|
33046
34044
|
result;
|
|
33047
34045
|
constructor(result) {
|
|
@@ -42950,6 +43948,13 @@ __export(index_exports, {
|
|
|
42950
43948
|
AgentMessageDirection: () => AgentMessageDirection,
|
|
42951
43949
|
AiRunner: () => AiRunner,
|
|
42952
43950
|
BACKUP_JOB_ID: () => BACKUP_JOB_ID,
|
|
43951
|
+
CODEX_APP_DIRS_ENV: () => CODEX_APP_DIRS_ENV,
|
|
43952
|
+
CODEX_AUTH_PROBE_TIMEOUT_MS: () => CODEX_AUTH_PROBE_TIMEOUT_MS,
|
|
43953
|
+
CODEX_COMMAND_SOURCES: () => CODEX_COMMAND_SOURCES,
|
|
43954
|
+
CODEX_PROBE_TIMEOUT_MS: () => CODEX_PROBE_TIMEOUT_MS,
|
|
43955
|
+
CODEX_READINESS_CACHE_TTL_MS: () => CODEX_READINESS_CACHE_TTL_MS,
|
|
43956
|
+
CODEX_READINESS_STATES: () => CODEX_READINESS_STATES,
|
|
43957
|
+
CODEX_RECOVERY_ACTIONS: () => CODEX_RECOVERY_ACTIONS,
|
|
42953
43958
|
CommandStore: () => CommandStore,
|
|
42954
43959
|
DEFAULT_AI_PERMISSION_PRESET: () => DEFAULT_AI_PERMISSION_PRESET,
|
|
42955
43960
|
DEFAULT_OFFLINE_REPLAY_INTERVAL_SEC: () => DEFAULT_OFFLINE_REPLAY_INTERVAL_SEC,
|
|
@@ -42959,10 +43964,13 @@ __export(index_exports, {
|
|
|
42959
43964
|
HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS: () => HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS,
|
|
42960
43965
|
InboundReplayGate: () => InboundReplayGate,
|
|
42961
43966
|
InvalidXmtpMessageStore: () => InvalidXmtpMessageStore,
|
|
43967
|
+
JOB_PROVIDER_BINDING_SOURCES: () => JOB_PROVIDER_BINDING_SOURCES,
|
|
43968
|
+
LogEvent: () => LogEvent,
|
|
42962
43969
|
MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG: () => MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG,
|
|
42963
43970
|
MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN: () => MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
|
|
42964
43971
|
NATIVE_LAUNCHER_EXE_NAME: () => NATIVE_LAUNCHER_EXE_NAME,
|
|
42965
43972
|
OPENCLAW_GATEWAY_ROUTE_GROUP_ID: () => OPENCLAW_GATEWAY_ROUTE_GROUP_ID,
|
|
43973
|
+
ProviderNotReadyError: () => ProviderNotReadyError,
|
|
42966
43974
|
SYSTEM_NOTIFICATION_SESSION_KEY: () => SYSTEM_NOTIFICATION_SESSION_KEY,
|
|
42967
43975
|
SessionBusyTracker: () => SessionBusyTracker,
|
|
42968
43976
|
SessionStore: () => SessionStore,
|
|
@@ -42983,15 +43991,20 @@ __export(index_exports, {
|
|
|
42983
43991
|
activeDaemonPointerPath: () => activeDaemonPointerPath,
|
|
42984
43992
|
aiRunSentryExtra: () => aiRunSentryExtra,
|
|
42985
43993
|
assertHermesSetupSupportedOnPlatform: () => assertHermesSetupSupportedOnPlatform,
|
|
43994
|
+
authStatusFromReason: () => authStatusFromReason,
|
|
42986
43995
|
bindJobProviderToCurrentDefaultIfMissing: () => bindJobProviderToCurrentDefaultIfMissing,
|
|
43996
|
+
bindJobProviderToCurrentDefaultIfReady: () => bindJobProviderToCurrentDefaultIfReady,
|
|
42987
43997
|
bindJobProviderToCurrentRuntimeIfMissing: () => bindJobProviderToCurrentRuntimeIfMissing,
|
|
43998
|
+
bindJobProviderToCurrentRuntimeIfReady: () => bindJobProviderToCurrentRuntimeIfReady,
|
|
42988
43999
|
bindMessageJobProviderToCurrentDefault: () => bindMessageJobProviderToCurrentDefault,
|
|
44000
|
+
bindNewJobProviderIfReady: () => bindNewJobProviderIfReady,
|
|
42989
44001
|
bindOpenClawGatewayRouteFromEnv: () => bindOpenClawGatewayRouteFromEnv,
|
|
42990
44002
|
buildAgentMessageNotice: () => buildAgentMessageNotice,
|
|
42991
44003
|
buildAiAdapterCommand: () => buildAiAdapterCommand,
|
|
42992
44004
|
buildAiProviderEnv: () => buildAiProviderEnv,
|
|
42993
44005
|
buildAutostartVbs: () => buildAutostartVbs,
|
|
42994
44006
|
buildBackupJobSessionKey: () => buildBackupJobSessionKey,
|
|
44007
|
+
buildCodexRecoveryGuidance: () => buildCodexRecoveryGuidance,
|
|
42995
44008
|
buildDispatchSessionKey: () => buildDispatchSessionKey,
|
|
42996
44009
|
buildDoctorSentryExtra: () => buildDoctorSentryExtra,
|
|
42997
44010
|
buildExternalCommandInvocation: () => buildExternalCommandInvocation,
|
|
@@ -43020,7 +44033,15 @@ __export(index_exports, {
|
|
|
43020
44033
|
callSessionsCreate: () => callSessionsCreate,
|
|
43021
44034
|
callSessionsDelete: () => callSessionsDelete,
|
|
43022
44035
|
callSessionsSend: () => callSessionsSend,
|
|
44036
|
+
checkClaudeCliAuthStatus: () => checkClaudeCliAuthStatus,
|
|
44037
|
+
checkCodexCliAuthStatus: () => checkCodexCliAuthStatus,
|
|
44038
|
+
checkCodexReadiness: () => checkCodexReadiness,
|
|
43023
44039
|
claimActiveDaemon: () => claimActiveDaemon,
|
|
44040
|
+
classifyJobBindingSource: () => classifyJobBindingSource,
|
|
44041
|
+
clearCodexReadinessCache: () => clearCodexReadinessCache,
|
|
44042
|
+
codexCommandResolvesWithoutOverride: () => codexCommandResolvesWithoutOverride,
|
|
44043
|
+
codexReadinessCacheKey: () => codexReadinessCacheKey,
|
|
44044
|
+
collectCodexAppBundledCandidates: () => collectCodexAppBundledCandidates,
|
|
43024
44045
|
collectCodexCommandCandidates: () => collectCodexCommandCandidates,
|
|
43025
44046
|
commandExists: () => commandExists,
|
|
43026
44047
|
createAiToolFailureTracker: () => createAiToolFailureTracker,
|
|
@@ -43036,6 +44057,7 @@ __export(index_exports, {
|
|
|
43036
44057
|
ensureDaemonReady: () => ensureDaemonReady,
|
|
43037
44058
|
ensureDefaultAiProvider: () => ensureDefaultAiProvider,
|
|
43038
44059
|
ensureOpenClawOkxA2aPluginConfig: () => ensureOpenClawOkxA2aPluginConfig,
|
|
44060
|
+
ensureProviderReadyForBinding: () => ensureProviderReadyForBinding,
|
|
43039
44061
|
ensureWindowsNativeLauncher: () => ensureWindowsNativeLauncher,
|
|
43040
44062
|
evictPid: () => evictPid,
|
|
43041
44063
|
exportDiagnosticLogs: () => exportDiagnosticLogs,
|
|
@@ -43063,6 +44085,7 @@ __export(index_exports, {
|
|
|
43063
44085
|
isDefaultTaskHome: () => isDefaultTaskHome,
|
|
43064
44086
|
isGatewayAvailableForHeartbeat: () => isGatewayAvailableForHeartbeat,
|
|
43065
44087
|
isHermesGatewayPluginEnabled: () => isHermesGatewayPluginEnabled,
|
|
44088
|
+
isInfoEventAllowlisted: () => isInfoEventAllowlisted,
|
|
43066
44089
|
isOfflineReplayForTrigger: () => isOfflineReplayForTrigger,
|
|
43067
44090
|
isOpenClawA2aPluginAvailable: () => isOpenClawA2aPluginAvailable,
|
|
43068
44091
|
isOpenClawGatewayAvailable: () => isOpenClawGatewayAvailable,
|
|
@@ -43088,11 +44111,15 @@ __export(index_exports, {
|
|
|
43088
44111
|
parsePluginYamlVersion: () => parsePluginYamlVersion,
|
|
43089
44112
|
parseWindowsParentProcessJson: () => parseWindowsParentProcessJson,
|
|
43090
44113
|
performRuntimeSwitch: () => performRuntimeSwitch,
|
|
44114
|
+
persistCurrentRuntimeSelectionForNewJob: () => persistCurrentRuntimeSelectionForNewJob,
|
|
43091
44115
|
pickOnchainosWin32Candidate: () => pickOnchainosWin32Candidate,
|
|
44116
|
+
prepareAndBindCurrentRuntimeForNewJob: () => prepareAndBindCurrentRuntimeForNewJob,
|
|
43092
44117
|
printLogsExportUsage: () => printLogsExportUsage,
|
|
43093
44118
|
printXmtpTestUsage: () => printXmtpTestUsage,
|
|
44119
|
+
probeCodexAuthStatus: () => probeCodexAuthStatus,
|
|
43094
44120
|
processFileMessage: () => processFileMessage,
|
|
43095
44121
|
readAiProviderTimeoutMs: () => readAiProviderTimeoutMs,
|
|
44122
|
+
readCachedCodexReadiness: () => readCachedCodexReadiness,
|
|
43096
44123
|
readLastLines: () => readLastLines,
|
|
43097
44124
|
readParentProcessCommandForPlatform: () => readParentProcessCommandForPlatform,
|
|
43098
44125
|
readUserAttentionWatcherEvent: () => readUserAttentionWatcherEvent,
|
|
@@ -43100,14 +44127,19 @@ __export(index_exports, {
|
|
|
43100
44127
|
registerUserAttentionWatcher: () => registerUserAttentionWatcher,
|
|
43101
44128
|
releaseActiveDaemon: () => releaseActiveDaemon,
|
|
43102
44129
|
removeUserAttentionWatcher: () => removeUserAttentionWatcher,
|
|
44130
|
+
replayScanBackoffMs: () => replayScanBackoffMs,
|
|
43103
44131
|
reportDoctorRunToSentry: () => reportDoctorRunToSentry,
|
|
43104
44132
|
reportUserNotificationFailure: () => reportUserNotificationFailure,
|
|
43105
44133
|
resetMessageEligibleOfflineReplayCapabilityForTests: () => resetMessageEligibleOfflineReplayCapabilityForTests,
|
|
43106
44134
|
resetResolvedOnchainosBinForTests: () => resetResolvedOnchainosBinForTests,
|
|
43107
44135
|
resolveAiPermissionPreset: () => resolveAiPermissionPreset,
|
|
43108
44136
|
resolveAiProviderCommand: () => resolveAiProviderCommand,
|
|
44137
|
+
resolveAiProviderCommandWithSource: () => resolveAiProviderCommandWithSource,
|
|
43109
44138
|
resolveAiProviderForDispatch: () => resolveAiProviderForDispatch,
|
|
44139
|
+
resolveAiProviderForDispatchWithReadiness: () => resolveAiProviderForDispatchWithReadiness,
|
|
44140
|
+
resolveCodexCommandCandidates: () => resolveCodexCommandCandidates,
|
|
43110
44141
|
resolveCodexCommandPath: () => resolveCodexCommandPath,
|
|
44142
|
+
resolveCodexCommandSource: () => resolveCodexCommandSource,
|
|
43111
44143
|
resolveConfiguredAiProvider: () => resolveConfiguredAiProvider,
|
|
43112
44144
|
resolveConfiguredAiProviderForJob: () => resolveConfiguredAiProviderForJob,
|
|
43113
44145
|
resolveDirectCommunicationSessionTarget: () => resolveDirectCommunicationSessionTarget,
|
|
@@ -43119,6 +44151,7 @@ __export(index_exports, {
|
|
|
43119
44151
|
resolveOpenClawGatewayConfig: () => resolveOpenClawGatewayConfig,
|
|
43120
44152
|
resolveOpenClawGatewayRoute: () => resolveOpenClawGatewayRoute,
|
|
43121
44153
|
resolveOpenClawGatewayRoutes: () => resolveOpenClawGatewayRoutes,
|
|
44154
|
+
resolveProviderCommandWithSelfHeal: () => resolveProviderCommandWithSelfHeal,
|
|
43122
44155
|
resolveSystemNotificationTargets: () => resolveSystemNotificationTargets,
|
|
43123
44156
|
resolveTaskConfigPath: () => resolveTaskConfigPath,
|
|
43124
44157
|
resolveTaskHome: () => resolveTaskHome,
|
|
@@ -43146,6 +44179,8 @@ __export(index_exports, {
|
|
|
43146
44179
|
subscribeUserAttentionEvents: () => subscribeUserAttentionEvents,
|
|
43147
44180
|
summarizeXmtpTestEvents: () => summarizeXmtpTestEvents,
|
|
43148
44181
|
switchProvider: () => switchProvider,
|
|
44182
|
+
switchProviderWithReadinessGate: () => switchProviderWithReadinessGate,
|
|
44183
|
+
switchProviderWithReadinessGateCore: () => switchProviderWithReadinessGateCore,
|
|
43149
44184
|
systemdUnitHasCurrentRestartPolicy: () => systemdUnitHasCurrentRestartPolicy,
|
|
43150
44185
|
toOpenClawGatewaySessionKey: () => toOpenClawGatewaySessionKey,
|
|
43151
44186
|
toWindowsInvocation: () => toWindowsInvocation,
|
|
@@ -43455,8 +44490,8 @@ init_paths();
|
|
|
43455
44490
|
init_session_store();
|
|
43456
44491
|
|
|
43457
44492
|
// src/task-config.ts
|
|
43458
|
-
var
|
|
43459
|
-
var
|
|
44493
|
+
var import_node_fs9 = require("node:fs");
|
|
44494
|
+
var import_node_path13 = require("node:path");
|
|
43460
44495
|
init_paths();
|
|
43461
44496
|
var AI_PERMISSION_PRESETS = ["bypass", "auto"];
|
|
43462
44497
|
var DEFAULT_AI_PERMISSION_PRESET = "bypass";
|
|
@@ -43472,23 +44507,23 @@ function resolveAiPermissionPreset(options = {}) {
|
|
|
43472
44507
|
}
|
|
43473
44508
|
function readAiPermissionPresetFromConfig(homeDir) {
|
|
43474
44509
|
const configPath = resolveTaskConfigPath(homeDir);
|
|
43475
|
-
if (!(0,
|
|
44510
|
+
if (!(0, import_node_fs9.existsSync)(configPath)) {
|
|
43476
44511
|
return null;
|
|
43477
44512
|
}
|
|
43478
|
-
const raw = readSimpleTomlStringValue((0,
|
|
44513
|
+
const raw = readSimpleTomlStringValue((0, import_node_fs9.readFileSync)(configPath, "utf8"), "ai.permissions", "preset");
|
|
43479
44514
|
return raw ? normalizeAiPermissionPreset(raw, configPath) : null;
|
|
43480
44515
|
}
|
|
43481
44516
|
function writeAiPermissionPresetToConfig(homeDir, preset) {
|
|
43482
44517
|
const normalized = normalizeAiPermissionPreset(preset, "permission preset");
|
|
43483
44518
|
ensureTaskDir(homeDir);
|
|
43484
44519
|
const configPath = resolveTaskConfigPath(homeDir);
|
|
43485
|
-
const current = (0,
|
|
44520
|
+
const current = (0, import_node_fs9.existsSync)(configPath) ? (0, import_node_fs9.readFileSync)(configPath, "utf8") : "";
|
|
43486
44521
|
const next = upsertSimpleTomlStringValue(current, "ai.permissions", "preset", normalized);
|
|
43487
|
-
(0,
|
|
44522
|
+
(0, import_node_fs9.writeFileSync)(configPath, next, "utf8");
|
|
43488
44523
|
return normalized;
|
|
43489
44524
|
}
|
|
43490
44525
|
function resolveTaskConfigPath(homeDir) {
|
|
43491
|
-
return (0,
|
|
44526
|
+
return (0, import_node_path13.join)(homeDir, "config.toml");
|
|
43492
44527
|
}
|
|
43493
44528
|
function normalizeAiPermissionPreset(value, source) {
|
|
43494
44529
|
const normalized = value.trim().toLowerCase();
|
|
@@ -43598,11 +44633,14 @@ function buildCodexPermissionPrefix(homeDir, cwd, env = process.env, permissionP
|
|
|
43598
44633
|
cwd
|
|
43599
44634
|
];
|
|
43600
44635
|
}
|
|
44636
|
+
function resolveAdapterProviderCommand(options, homeDir) {
|
|
44637
|
+
const storedCommand = options.provider === "codex" && options.homeDir ? readStoredAiProviderCommand(options.provider, homeDir) : null;
|
|
44638
|
+
return resolveAiProviderCommandWithSource(options.provider, options.env ?? process.env, storedCommand);
|
|
44639
|
+
}
|
|
43601
44640
|
function buildAiAdapterCommand(options) {
|
|
43602
44641
|
const env = options.env ?? process.env;
|
|
43603
44642
|
const homeDir = options.homeDir ?? resolveTaskPaths().homeDir;
|
|
43604
|
-
const
|
|
43605
|
-
const command = resolveAiProviderCommand(options.provider, env, storedCommand);
|
|
44643
|
+
const { command } = resolveAdapterProviderCommand(options, homeDir);
|
|
43606
44644
|
const template = readAdapterArgsTemplate(options.provider, !!options.sessionId, env);
|
|
43607
44645
|
if (template) {
|
|
43608
44646
|
return {
|
|
@@ -43774,6 +44812,7 @@ async function runAiAdapter(options) {
|
|
|
43774
44812
|
);
|
|
43775
44813
|
if (timedOut || exitCode !== 0 || !sessionId) {
|
|
43776
44814
|
const reason = timedOut ? "timeout" : !sessionId ? "missing_ai_session_id" : "non_zero_exit";
|
|
44815
|
+
const { source: commandSource } = resolveAdapterProviderCommand({ ...options, homeDir }, homeDir);
|
|
43777
44816
|
logger.error(
|
|
43778
44817
|
timedOut ? LogEvent.AI_RUN_TIMEOUT : LogEvent.AI_RUN_FAILED,
|
|
43779
44818
|
new Error(`${options.provider} AI adapter failed: ${reason}`),
|
|
@@ -43783,6 +44822,8 @@ async function runAiAdapter(options) {
|
|
|
43783
44822
|
provider: options.provider,
|
|
43784
44823
|
stage: "run_adapter",
|
|
43785
44824
|
reason,
|
|
44825
|
+
// Category only — never the resolved command, its path or its arguments.
|
|
44826
|
+
commandSource: commandSource ?? "",
|
|
43786
44827
|
sessionKey: options.sessionKey ?? "",
|
|
43787
44828
|
timeoutMs: timeoutMs === null ? "" : String(timeoutMs),
|
|
43788
44829
|
exitCode: exitCode === null ? "null" : String(exitCode),
|
|
@@ -44171,6 +45212,10 @@ function aiRunSentryExtra(input) {
|
|
|
44171
45212
|
stage: input.stage ?? "",
|
|
44172
45213
|
checkpoint: input.checkpoint ?? "",
|
|
44173
45214
|
outcome: input.outcome ?? "",
|
|
45215
|
+
commandSource: input.commandSource ?? "",
|
|
45216
|
+
jobBindingSource: input.jobBindingSource ?? "",
|
|
45217
|
+
recoveryAction: input.recoveryAction ?? "",
|
|
45218
|
+
readinessState: input.readinessState ?? "",
|
|
44174
45219
|
mode: input.mode ?? "",
|
|
44175
45220
|
pid: input.pid ?? "",
|
|
44176
45221
|
runAttempt: input.runAttempt ?? "",
|
|
@@ -44206,6 +45251,15 @@ function aiRunDeliveryId(input) {
|
|
|
44206
45251
|
sessionKey: input.sessionKey
|
|
44207
45252
|
});
|
|
44208
45253
|
}
|
|
45254
|
+
function resolveProviderCommandWithSelfHeal(options) {
|
|
45255
|
+
const env = options.env ?? process.env;
|
|
45256
|
+
const storedCommand = options.provider === "codex" ? options.store.getAiProviderCommand(options.provider) : null;
|
|
45257
|
+
const resolved = resolveAiProviderCommandWithSource(options.provider, env, storedCommand, options.platform);
|
|
45258
|
+
if (options.provider === "codex" && resolved.source !== "explicit_override" && resolved.command !== storedCommand && (0, import_node_path16.isAbsolute)(resolved.command)) {
|
|
45259
|
+
options.store.setAiProviderCommand(options.provider, resolved.command);
|
|
45260
|
+
}
|
|
45261
|
+
return { command: resolved.command, commandSource: resolved.source };
|
|
45262
|
+
}
|
|
44209
45263
|
var AiRunner = class {
|
|
44210
45264
|
homeDir;
|
|
44211
45265
|
logsDir;
|
|
@@ -44351,11 +45405,41 @@ var AiRunner = class {
|
|
|
44351
45405
|
storeReadMs = storeReadEndedAt - storeReadStartedAt;
|
|
44352
45406
|
}
|
|
44353
45407
|
const resolveStartedAt = Date.now();
|
|
44354
|
-
const
|
|
44355
|
-
|
|
44356
|
-
|
|
44357
|
-
|
|
44358
|
-
|
|
45408
|
+
const jobBinding = this.readJobProviderBindingForTelemetry(request.jobId);
|
|
45409
|
+
let provider;
|
|
45410
|
+
try {
|
|
45411
|
+
provider = await resolveAiProviderForDispatchWithReadiness({
|
|
45412
|
+
store: this.sessionStore,
|
|
45413
|
+
sessionKey: request.sessionKey,
|
|
45414
|
+
jobId: request.jobId
|
|
45415
|
+
});
|
|
45416
|
+
} catch (err) {
|
|
45417
|
+
const notReady = err instanceof ProviderNotReadyError ? err : null;
|
|
45418
|
+
logger.error(
|
|
45419
|
+
LogEvent.AI_RUN_FAILED,
|
|
45420
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
45421
|
+
aiRunSentryExtra({
|
|
45422
|
+
source: request.source,
|
|
45423
|
+
sessionKey: request.sessionKey,
|
|
45424
|
+
jobId: request.jobId,
|
|
45425
|
+
agentId: request.agentId,
|
|
45426
|
+
messageId: request.messageId,
|
|
45427
|
+
deliveryId,
|
|
45428
|
+
provider: notReady?.provider ?? jobBinding.provider ?? "unknown",
|
|
45429
|
+
stage: "resolve_provider",
|
|
45430
|
+
reason: "provider_not_ready",
|
|
45431
|
+
checkpoint: "ai_run/provider_not_ready",
|
|
45432
|
+
outcome: "failed",
|
|
45433
|
+
runAttempt,
|
|
45434
|
+
jobBindingSource: jobBinding.bindingSource,
|
|
45435
|
+
// Categories only — never the resolved command or its path (NFR-3).
|
|
45436
|
+
readinessState: notReady?.readinessState ?? null,
|
|
45437
|
+
commandSource: notReady?.commandSource ?? null,
|
|
45438
|
+
recoveryAction: notReady?.recoveryAction ?? "run_setup"
|
|
45439
|
+
})
|
|
45440
|
+
);
|
|
45441
|
+
throw err;
|
|
45442
|
+
}
|
|
44359
45443
|
const sessionMeta = this.sessionStore.getSession(request.sessionKey);
|
|
44360
45444
|
const existingAiSessionId = this.readRunAiSessionId(provider, request, existing, sessionMeta);
|
|
44361
45445
|
const runMode = existingAiSessionId ? "resume" : "new";
|
|
@@ -44425,7 +45509,7 @@ var AiRunner = class {
|
|
|
44425
45509
|
}
|
|
44426
45510
|
}
|
|
44427
45511
|
};
|
|
44428
|
-
const command = this.resolveProviderCommand(provider);
|
|
45512
|
+
const { command, commandSource } = this.resolveProviderCommand(provider);
|
|
44429
45513
|
let stderrText = "";
|
|
44430
45514
|
let timedOut = false;
|
|
44431
45515
|
const timeoutMs = readAiProviderTimeoutMs();
|
|
@@ -44488,6 +45572,8 @@ var AiRunner = class {
|
|
|
44488
45572
|
provider,
|
|
44489
45573
|
checkpoint: "ai_run/started",
|
|
44490
45574
|
outcome: "pending",
|
|
45575
|
+
commandSource,
|
|
45576
|
+
jobBindingSource: jobBinding.bindingSource,
|
|
44491
45577
|
mode: runMode,
|
|
44492
45578
|
pid: child.pid ?? null,
|
|
44493
45579
|
runAttempt,
|
|
@@ -44601,6 +45687,11 @@ var AiRunner = class {
|
|
|
44601
45687
|
stage: "run_ai",
|
|
44602
45688
|
checkpoint: overrides?.checkpoint ?? "ai_run/failed",
|
|
44603
45689
|
outcome: overrides?.outcome ?? "failed",
|
|
45690
|
+
// Which tier produced the binary that failed, and how the job picked this
|
|
45691
|
+
// provider — the two dimensions that separate "GUI-only Codex was never
|
|
45692
|
+
// discovered" from "the CLI itself is broken".
|
|
45693
|
+
commandSource,
|
|
45694
|
+
jobBindingSource: jobBinding.bindingSource,
|
|
44604
45695
|
runAttempt,
|
|
44605
45696
|
timeoutMs,
|
|
44606
45697
|
exitCode,
|
|
@@ -44946,8 +46037,32 @@ var AiRunner = class {
|
|
|
44946
46037
|
return extractAiSessionId(provider, line);
|
|
44947
46038
|
}
|
|
44948
46039
|
resolveProviderCommand(provider) {
|
|
44949
|
-
|
|
44950
|
-
|
|
46040
|
+
return resolveProviderCommandWithSelfHeal({
|
|
46041
|
+
provider,
|
|
46042
|
+
store: this.sessionStore,
|
|
46043
|
+
env: process.env
|
|
46044
|
+
});
|
|
46045
|
+
}
|
|
46046
|
+
/**
|
|
46047
|
+
* Telemetry-only view of how this run's provider was decided. Never throws: a
|
|
46048
|
+
* store read that fails must not replace the error we are already reporting.
|
|
46049
|
+
*/
|
|
46050
|
+
readJobProviderBindingForTelemetry(jobId) {
|
|
46051
|
+
const normalizedJobId = normalizeOptionalText2(jobId);
|
|
46052
|
+
if (normalizedJobId) {
|
|
46053
|
+
try {
|
|
46054
|
+
const bound = this.sessionStore.getJobProviderBinding(normalizedJobId);
|
|
46055
|
+
if (bound) {
|
|
46056
|
+
return { provider: bound.provider, bindingSource: "existing_binding" };
|
|
46057
|
+
}
|
|
46058
|
+
} catch {
|
|
46059
|
+
}
|
|
46060
|
+
}
|
|
46061
|
+
try {
|
|
46062
|
+
return { provider: this.sessionStore.getDefaultAiProvider(), bindingSource: "default_provider" };
|
|
46063
|
+
} catch {
|
|
46064
|
+
return { provider: null, bindingSource: "default_provider" };
|
|
46065
|
+
}
|
|
44951
46066
|
}
|
|
44952
46067
|
formatRunTarget(request) {
|
|
44953
46068
|
if (request.source === "job-dispatch") {
|
|
@@ -54754,6 +55869,8 @@ function compareTimestamp(a, b) {
|
|
|
54754
55869
|
var DEFAULT_DATA_DIR = resolveA2aTaskPaths().xmtpDir;
|
|
54755
55870
|
var XMTP_INSTALLATION_WARNING_THRESHOLD = 2;
|
|
54756
55871
|
var XMTP_INSTALLATION_NEAR_LIMIT_THRESHOLD = 8;
|
|
55872
|
+
var REPLAY_SCAN_INITIAL_BACKOFF_MS = 3e4;
|
|
55873
|
+
var REPLAY_SCAN_MAX_BACKOFF_MS = 5 * 6e4;
|
|
54757
55874
|
function cachePath(dataDir, fileName) {
|
|
54758
55875
|
return (0, import_node_path21.join)(dataDir, fileName);
|
|
54759
55876
|
}
|
|
@@ -55238,6 +56355,13 @@ function parseGroupPayload(content) {
|
|
|
55238
56355
|
return null;
|
|
55239
56356
|
}
|
|
55240
56357
|
}
|
|
56358
|
+
function replayScanBackoffMs(consecutiveFailures) {
|
|
56359
|
+
const exponent = Math.max(0, Math.floor(consecutiveFailures) - 1);
|
|
56360
|
+
return Math.min(
|
|
56361
|
+
REPLAY_SCAN_INITIAL_BACKOFF_MS * 2 ** exponent,
|
|
56362
|
+
REPLAY_SCAN_MAX_BACKOFF_MS
|
|
56363
|
+
);
|
|
56364
|
+
}
|
|
55241
56365
|
function isOfflineReplayForTrigger(trigger) {
|
|
55242
56366
|
return trigger === "startup";
|
|
55243
56367
|
}
|
|
@@ -55273,6 +56397,11 @@ var XmtpService = class _XmtpService {
|
|
|
55273
56397
|
dataDir = DEFAULT_DATA_DIR;
|
|
55274
56398
|
syncByAddress = /* @__PURE__ */ new Map();
|
|
55275
56399
|
offlineReplaySingleFlight = new KeyedSingleFlight();
|
|
56400
|
+
replayScanFailuresByAddress = /* @__PURE__ */ new Map();
|
|
56401
|
+
replayScanRetryByAddress = /* @__PURE__ */ new Map();
|
|
56402
|
+
replayScanNowMs = () => Date.now();
|
|
56403
|
+
replayScanSetTimeout = (callback, delayMs) => setTimeout(callback, delayMs);
|
|
56404
|
+
replayScanClearTimeout = (timer) => clearTimeout(timer);
|
|
55276
56405
|
inboundReplayGates = /* @__PURE__ */ new Map();
|
|
55277
56406
|
streamRecoveryCleanupByAddress = /* @__PURE__ */ new Map();
|
|
55278
56407
|
nextClientGeneration = 1;
|
|
@@ -55751,6 +56880,8 @@ var XmtpService = class _XmtpService {
|
|
|
55751
56880
|
}
|
|
55752
56881
|
this.clients.delete(address);
|
|
55753
56882
|
this.inboundReplayGates.delete(address.toLowerCase());
|
|
56883
|
+
this.replayScanFailuresByAddress.delete(address.toLowerCase());
|
|
56884
|
+
this.clearReplayScanRetry(address);
|
|
55754
56885
|
logger.info(LogEvent.AGENT_CLIENT_REMOVED, {
|
|
55755
56886
|
...agentExtras({
|
|
55756
56887
|
walletAddress: address,
|
|
@@ -55965,6 +57096,8 @@ var XmtpService = class _XmtpService {
|
|
|
55965
57096
|
await Promise.resolve(oldClient.stop());
|
|
55966
57097
|
this.clients.delete(address);
|
|
55967
57098
|
this.inboundReplayGates.delete(addressKey);
|
|
57099
|
+
this.replayScanFailuresByAddress.delete(addressKey);
|
|
57100
|
+
this.clearReplayScanRetry(address);
|
|
55968
57101
|
logWithTimestamp(
|
|
55969
57102
|
`[xmtp-sdk] recycle: stopped client: ${address} (agentId=${agentInfo.agentId})`
|
|
55970
57103
|
);
|
|
@@ -56514,6 +57647,60 @@ var XmtpService = class _XmtpService {
|
|
|
56514
57647
|
this.clientGenerationByClient.set(client, generation);
|
|
56515
57648
|
return generation;
|
|
56516
57649
|
}
|
|
57650
|
+
clearReplayScanRetry(address) {
|
|
57651
|
+
const addressKey = address.toLowerCase();
|
|
57652
|
+
const scheduled = this.replayScanRetryByAddress.get(addressKey);
|
|
57653
|
+
if (!scheduled) {
|
|
57654
|
+
return;
|
|
57655
|
+
}
|
|
57656
|
+
this.replayScanRetryByAddress.delete(addressKey);
|
|
57657
|
+
this.replayScanClearTimeout(scheduled.timer);
|
|
57658
|
+
}
|
|
57659
|
+
scheduleReplayScanRetry(address, expectedClient, clientGeneration, retryTrigger, delayMs) {
|
|
57660
|
+
const addressKey = address.toLowerCase();
|
|
57661
|
+
const scheduled = this.replayScanRetryByAddress.get(addressKey);
|
|
57662
|
+
if (scheduled?.clientGeneration === clientGeneration) {
|
|
57663
|
+
return;
|
|
57664
|
+
}
|
|
57665
|
+
if (scheduled) {
|
|
57666
|
+
this.clearReplayScanRetry(address);
|
|
57667
|
+
}
|
|
57668
|
+
const timer = this.replayScanSetTimeout(() => {
|
|
57669
|
+
const current = this.replayScanRetryByAddress.get(addressKey);
|
|
57670
|
+
if (current?.timer !== timer) {
|
|
57671
|
+
return;
|
|
57672
|
+
}
|
|
57673
|
+
this.replayScanRetryByAddress.delete(addressKey);
|
|
57674
|
+
if (this.clients.get(address) !== expectedClient || this.stoppedAddresses.has(addressKey)) {
|
|
57675
|
+
return;
|
|
57676
|
+
}
|
|
57677
|
+
void this.replayOfflineMessagesForAddressSingleFlight(
|
|
57678
|
+
address,
|
|
57679
|
+
retryTrigger,
|
|
57680
|
+
void 0,
|
|
57681
|
+
expectedClient
|
|
57682
|
+
).catch((err) => {
|
|
57683
|
+
logWithTimestamp(
|
|
57684
|
+
`[xmtp-sdk:replay:${address}] scheduled replay scan retry failed:`,
|
|
57685
|
+
err
|
|
57686
|
+
);
|
|
57687
|
+
logger.error(
|
|
57688
|
+
LogEvent.OFFLINE_REPLAY_FAILED,
|
|
57689
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
57690
|
+
{
|
|
57691
|
+
walletAddress: address,
|
|
57692
|
+
stage: "offlineReplay/scheduledRetry",
|
|
57693
|
+
outcome: "failed"
|
|
57694
|
+
}
|
|
57695
|
+
);
|
|
57696
|
+
});
|
|
57697
|
+
}, Math.max(0, delayMs));
|
|
57698
|
+
timer.unref?.();
|
|
57699
|
+
this.replayScanRetryByAddress.set(addressKey, {
|
|
57700
|
+
clientGeneration,
|
|
57701
|
+
timer
|
|
57702
|
+
});
|
|
57703
|
+
}
|
|
56517
57704
|
notifyAgentsWakeupAfterReplay() {
|
|
56518
57705
|
const notifyAgentsWakeup2 = this.tools.notifyAgentsWakeup;
|
|
56519
57706
|
const agentIds = this.allAgents.map((agent) => agent.agentId);
|
|
@@ -56752,7 +57939,7 @@ var XmtpService = class _XmtpService {
|
|
|
56752
57939
|
String(recovery.recoverySequence),
|
|
56753
57940
|
agent
|
|
56754
57941
|
);
|
|
56755
|
-
if (!recoveryDisposed && this.clients.get(address) === agent) {
|
|
57942
|
+
if (summary.outcome === "completed" && !recoveryDisposed && this.clients.get(address) === agent) {
|
|
56756
57943
|
logger.info(LogEvent.AGENT_STREAM_RECOVERY_REPLAY_COMPLETED, {
|
|
56757
57944
|
...agentExtras(identity),
|
|
56758
57945
|
clientGeneration: String(clientGeneration),
|
|
@@ -56760,7 +57947,9 @@ var XmtpService = class _XmtpService {
|
|
|
56760
57947
|
replayed: String(summary.replayed),
|
|
56761
57948
|
skipped: String(summary.skipped),
|
|
56762
57949
|
conversationCount: String(summary.conversations),
|
|
56763
|
-
replayDurationMs: String(summary.durationMs)
|
|
57950
|
+
replayDurationMs: String(summary.durationMs),
|
|
57951
|
+
outcome: "success",
|
|
57952
|
+
replayOutcome: summary.outcome
|
|
56764
57953
|
});
|
|
56765
57954
|
}
|
|
56766
57955
|
} catch (err) {
|
|
@@ -56826,10 +58015,6 @@ var XmtpService = class _XmtpService {
|
|
|
56826
58015
|
summary.durationMs = Date.now() - addressReplayStartedAt;
|
|
56827
58016
|
return summary;
|
|
56828
58017
|
}
|
|
56829
|
-
const sentAfterNs = BigInt(lastSyncMs) * 1000000n;
|
|
56830
|
-
logWithTimestamp(
|
|
56831
|
-
`${tag} offline_replay_start agentId=${this.getAgentByAddress(address)?.agentId ?? ""} address=${address} since=${formatLogTimestamp2(lastSyncMs)} sinceMs=${lastSyncMs}`
|
|
56832
|
-
);
|
|
56833
58018
|
const myInboxId = agent.client.inboxId;
|
|
56834
58019
|
const handlerDeps = {
|
|
56835
58020
|
myXmtpAddress: address,
|
|
@@ -56844,7 +58029,87 @@ var XmtpService = class _XmtpService {
|
|
|
56844
58029
|
summary.durationMs = Date.now() - addressReplayStartedAt;
|
|
56845
58030
|
return summary;
|
|
56846
58031
|
}
|
|
58032
|
+
const addressKey = address.toLowerCase();
|
|
58033
|
+
const clientGeneration = this.getClientGeneration(agent);
|
|
58034
|
+
const attemptNowMs = this.replayScanNowMs();
|
|
58035
|
+
let priorFailure = this.replayScanFailuresByAddress.get(addressKey);
|
|
58036
|
+
if (priorFailure && priorFailure.clientGeneration !== clientGeneration) {
|
|
58037
|
+
this.replayScanFailuresByAddress.delete(addressKey);
|
|
58038
|
+
this.clearReplayScanRetry(address);
|
|
58039
|
+
priorFailure = void 0;
|
|
58040
|
+
}
|
|
58041
|
+
if (priorFailure && attemptNowMs < priorFailure.nextAttemptAtMs) {
|
|
58042
|
+
priorFailure.suppressedAttempts++;
|
|
58043
|
+
summary.outcome = "suppressed_backoff";
|
|
58044
|
+
summary.failureStage = priorFailure.lastFailureStage;
|
|
58045
|
+
summary.consecutiveFailures = priorFailure.consecutiveFailures;
|
|
58046
|
+
summary.backoffMs = priorFailure.nextAttemptAtMs - attemptNowMs;
|
|
58047
|
+
summary.suppressedAttempts = priorFailure.suppressedAttempts;
|
|
58048
|
+
if (priorFailure.suppressedAttempts === 1) {
|
|
58049
|
+
logWithTimestamp(
|
|
58050
|
+
`${tag} replay scan suppressed stage=${priorFailure.lastFailureStage} consecutiveFailures=${priorFailure.consecutiveFailures} retryInMs=${summary.backoffMs}`
|
|
58051
|
+
);
|
|
58052
|
+
}
|
|
58053
|
+
this.scheduleReplayScanRetry(
|
|
58054
|
+
address,
|
|
58055
|
+
agent,
|
|
58056
|
+
clientGeneration,
|
|
58057
|
+
priorFailure.retryTrigger,
|
|
58058
|
+
summary.backoffMs
|
|
58059
|
+
);
|
|
58060
|
+
summary.durationMs = Date.now() - addressReplayStartedAt;
|
|
58061
|
+
return summary;
|
|
58062
|
+
}
|
|
58063
|
+
const sentAfterNs = BigInt(lastSyncMs) * 1000000n;
|
|
58064
|
+
logWithTimestamp(
|
|
58065
|
+
`${tag} offline_replay_start agentId=${this.getAgentByAddress(address)?.agentId ?? ""} address=${address} since=${formatLogTimestamp2(lastSyncMs)} sinceMs=${lastSyncMs}`
|
|
58066
|
+
);
|
|
56847
58067
|
let conversations;
|
|
58068
|
+
const recordScanFailure = (stage, err) => {
|
|
58069
|
+
const previous = this.replayScanFailuresByAddress.get(addressKey);
|
|
58070
|
+
const consecutiveFailures = (previous?.consecutiveFailures ?? 0) + 1;
|
|
58071
|
+
const backoffMs = replayScanBackoffMs(consecutiveFailures);
|
|
58072
|
+
const suppressedAttempts = previous?.suppressedAttempts ?? 0;
|
|
58073
|
+
const failureNowMs = this.replayScanNowMs();
|
|
58074
|
+
const retryTrigger = isOfflineReplayForTrigger(trigger) ? "startup" : "periodic_repair";
|
|
58075
|
+
this.replayScanFailuresByAddress.set(addressKey, {
|
|
58076
|
+
clientGeneration,
|
|
58077
|
+
consecutiveFailures,
|
|
58078
|
+
nextAttemptAtMs: failureNowMs + backoffMs,
|
|
58079
|
+
retryTrigger,
|
|
58080
|
+
suppressedAttempts: 0,
|
|
58081
|
+
lastFailureStage: stage
|
|
58082
|
+
});
|
|
58083
|
+
this.scheduleReplayScanRetry(
|
|
58084
|
+
address,
|
|
58085
|
+
agent,
|
|
58086
|
+
clientGeneration,
|
|
58087
|
+
retryTrigger,
|
|
58088
|
+
backoffMs
|
|
58089
|
+
);
|
|
58090
|
+
summary.outcome = stage === "syncAll" ? "sync_failed" : "list_failed";
|
|
58091
|
+
summary.failureStage = stage;
|
|
58092
|
+
summary.consecutiveFailures = consecutiveFailures;
|
|
58093
|
+
summary.backoffMs = backoffMs;
|
|
58094
|
+
summary.suppressedAttempts = suppressedAttempts;
|
|
58095
|
+
logWithTimestamp(`${tag} conversation ${stage} failed, backing off:`, err);
|
|
58096
|
+
logger.error(
|
|
58097
|
+
LogEvent.OFFLINE_REPLAY_FAILED,
|
|
58098
|
+
err instanceof Error ? err : void 0,
|
|
58099
|
+
{
|
|
58100
|
+
walletAddress: address,
|
|
58101
|
+
stage: `offlineReplay/${stage}`,
|
|
58102
|
+
trigger,
|
|
58103
|
+
outcome: "failed",
|
|
58104
|
+
replayOutcome: summary.outcome,
|
|
58105
|
+
consecutiveFailures: String(consecutiveFailures),
|
|
58106
|
+
backoffMs: String(backoffMs),
|
|
58107
|
+
suppressedAttempts: String(suppressedAttempts)
|
|
58108
|
+
}
|
|
58109
|
+
);
|
|
58110
|
+
summary.durationMs = Date.now() - addressReplayStartedAt;
|
|
58111
|
+
return summary;
|
|
58112
|
+
};
|
|
56848
58113
|
try {
|
|
56849
58114
|
const syncAllStartedAt = Date.now();
|
|
56850
58115
|
await agent.client.conversations.syncAll([
|
|
@@ -56852,6 +58117,10 @@ var XmtpService = class _XmtpService {
|
|
|
56852
58117
|
import_node_bindings2.ConsentState.Unknown
|
|
56853
58118
|
]);
|
|
56854
58119
|
summary.syncAllMs = Date.now() - syncAllStartedAt;
|
|
58120
|
+
} catch (err) {
|
|
58121
|
+
return recordScanFailure("syncAll", err);
|
|
58122
|
+
}
|
|
58123
|
+
try {
|
|
56855
58124
|
const listStartedAt = Date.now();
|
|
56856
58125
|
conversations = await agent.client.conversations.list({
|
|
56857
58126
|
consentStates: [import_node_bindings2.ConsentState.Allowed, import_node_bindings2.ConsentState.Unknown]
|
|
@@ -56859,18 +58128,11 @@ var XmtpService = class _XmtpService {
|
|
|
56859
58128
|
summary.listMs = Date.now() - listStartedAt;
|
|
56860
58129
|
summary.conversations = conversations.length;
|
|
56861
58130
|
} catch (err) {
|
|
56862
|
-
|
|
56863
|
-
logger.error(
|
|
56864
|
-
LogEvent.OFFLINE_REPLAY_FAILED,
|
|
56865
|
-
err instanceof Error ? err : void 0,
|
|
56866
|
-
{
|
|
56867
|
-
walletAddress: address,
|
|
56868
|
-
stage: "syncAll/list"
|
|
56869
|
-
}
|
|
56870
|
-
);
|
|
56871
|
-
summary.durationMs = Date.now() - addressReplayStartedAt;
|
|
56872
|
-
return summary;
|
|
58131
|
+
return recordScanFailure("list", err);
|
|
56873
58132
|
}
|
|
58133
|
+
const recoveredFailure = this.replayScanFailuresByAddress.get(addressKey);
|
|
58134
|
+
this.replayScanFailuresByAddress.delete(addressKey);
|
|
58135
|
+
this.clearReplayScanRetry(address);
|
|
56874
58136
|
for (const conv of conversations) {
|
|
56875
58137
|
try {
|
|
56876
58138
|
const messagesStartedAt = Date.now();
|
|
@@ -57012,8 +58274,15 @@ var XmtpService = class _XmtpService {
|
|
|
57012
58274
|
replayed: String(summary.replayed),
|
|
57013
58275
|
skipped: String(summary.skipped),
|
|
57014
58276
|
conversationCount: String(summary.conversations),
|
|
57015
|
-
durationMs: String(Date.now() - addressReplayStartedAt)
|
|
58277
|
+
durationMs: String(Date.now() - addressReplayStartedAt),
|
|
58278
|
+
outcome: "success",
|
|
58279
|
+
replayOutcome: "completed",
|
|
58280
|
+
recoveredAfterFailures: String(recoveredFailure?.consecutiveFailures ?? 0),
|
|
58281
|
+
suppressedAttempts: String(recoveredFailure?.suppressedAttempts ?? 0)
|
|
57016
58282
|
});
|
|
58283
|
+
summary.outcome = "completed";
|
|
58284
|
+
summary.consecutiveFailures = recoveredFailure?.consecutiveFailures;
|
|
58285
|
+
summary.suppressedAttempts = recoveredFailure?.suppressedAttempts;
|
|
57017
58286
|
summary.durationMs = Date.now() - addressReplayStartedAt;
|
|
57018
58287
|
const newestSyncMs = this.syncByAddress.get(address) ?? lastSyncMs;
|
|
57019
58288
|
logWithTimestamp(
|
|
@@ -57080,6 +58349,7 @@ var XmtpService = class _XmtpService {
|
|
|
57080
58349
|
function createOfflineReplayAddressSummary(address) {
|
|
57081
58350
|
return {
|
|
57082
58351
|
address,
|
|
58352
|
+
outcome: "skipped",
|
|
57083
58353
|
replayed: 0,
|
|
57084
58354
|
skipped: 0,
|
|
57085
58355
|
conversations: 0,
|
|
@@ -58911,7 +60181,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
|
|
|
58911
60181
|
client: {
|
|
58912
60182
|
id: "gateway-client",
|
|
58913
60183
|
displayName: "okx-a2a-node",
|
|
58914
|
-
version: "0.2.
|
|
60184
|
+
version: "0.2.3-beta-6f2a8ec2c7-260810184309",
|
|
58915
60185
|
platform: "node",
|
|
58916
60186
|
mode: "backend",
|
|
58917
60187
|
instanceId
|
|
@@ -58922,7 +60192,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
|
|
|
58922
60192
|
commands: [],
|
|
58923
60193
|
permissions: {},
|
|
58924
60194
|
locale: Intl.DateTimeFormat().resolvedOptions().locale || "en-US",
|
|
58925
|
-
userAgent: `okx-a2a-node/${"0.2.
|
|
60195
|
+
userAgent: `okx-a2a-node/${"0.2.3-beta-6f2a8ec2c7-260810184309"}`,
|
|
58926
60196
|
auth: {
|
|
58927
60197
|
...config.token ? { token: config.token } : {},
|
|
58928
60198
|
...config.password ? { password: config.password } : {}
|
|
@@ -60519,15 +61789,21 @@ async function createGroupForAddress(params) {
|
|
|
60519
61789
|
{ groupName: params.groupName }
|
|
60520
61790
|
);
|
|
60521
61791
|
}
|
|
60522
|
-
function bindOutboundJobProviderToCurrentDefault(sessionStore, jobId) {
|
|
61792
|
+
async function bindOutboundJobProviderToCurrentDefault(sessionStore, jobId) {
|
|
60523
61793
|
if (!jobId || jobId === BACKUP_JOB_ID) {
|
|
60524
61794
|
return;
|
|
60525
61795
|
}
|
|
60526
61796
|
try {
|
|
60527
|
-
const result =
|
|
61797
|
+
const result = await bindNewJobProviderIfReady({
|
|
60528
61798
|
store: sessionStore,
|
|
60529
61799
|
jobId
|
|
60530
61800
|
});
|
|
61801
|
+
if (result?.degraded) {
|
|
61802
|
+
logWithTimestamp(
|
|
61803
|
+
`[okx-agent-task] job provider not ready from outbound-xmtp: job=${jobId} state=${result.readiness?.state ?? "not_required"} recovery=${result.readiness?.recoveryAction ?? "none"}; binding deferred`
|
|
61804
|
+
);
|
|
61805
|
+
return;
|
|
61806
|
+
}
|
|
60531
61807
|
if (result?.created) {
|
|
60532
61808
|
logWithTimestamp(`[okx-agent-task] job provider bound from outbound-xmtp: job=${jobId} provider=${result.provider}`);
|
|
60533
61809
|
}
|
|
@@ -61251,7 +62527,7 @@ async function handleXmtpSendCommand(params) {
|
|
|
61251
62527
|
const ownedStore2 = params.sessionStore ? null : new SessionStore();
|
|
61252
62528
|
const sessionStore2 = params.sessionStore ?? ownedStore2;
|
|
61253
62529
|
try {
|
|
61254
|
-
bindOutboundJobProviderToCurrentDefault(sessionStore2, command.jobId);
|
|
62530
|
+
await bindOutboundJobProviderToCurrentDefault(sessionStore2, command.jobId);
|
|
61255
62531
|
bindOutboundOpenClawRouteIfCurrent(sessionStore2, command.jobId, command.gatewaySessionKeys);
|
|
61256
62532
|
return await handleSqliteGroupSendCommand({
|
|
61257
62533
|
command,
|
|
@@ -61265,7 +62541,7 @@ async function handleXmtpSendCommand(params) {
|
|
|
61265
62541
|
const ownedStore = params.sessionStore ? null : new SessionStore();
|
|
61266
62542
|
const sessionStore = params.sessionStore ?? ownedStore;
|
|
61267
62543
|
try {
|
|
61268
|
-
bindOutboundJobProviderToCurrentDefault(sessionStore, command.jobId);
|
|
62544
|
+
await bindOutboundJobProviderToCurrentDefault(sessionStore, command.jobId);
|
|
61269
62545
|
bindOutboundOpenClawRouteIfCurrent(sessionStore, command.jobId, command.gatewaySessionKeys);
|
|
61270
62546
|
const { file, sessionAgentId } = await readFileForSend({
|
|
61271
62547
|
command,
|
|
@@ -63050,41 +64326,76 @@ function upsertDirectCommunicationSession(deps, target) {
|
|
|
63050
64326
|
toAgentXmtpAddress: remoteAgent?.communicationAddress ?? null
|
|
63051
64327
|
});
|
|
63052
64328
|
}
|
|
63053
|
-
function
|
|
64329
|
+
function neutralProviderGateResult(currentProvider) {
|
|
64330
|
+
return {
|
|
64331
|
+
allowed: true,
|
|
64332
|
+
created: false,
|
|
64333
|
+
provider: null,
|
|
64334
|
+
currentProvider,
|
|
64335
|
+
degraded: false,
|
|
64336
|
+
bindingSource: null,
|
|
64337
|
+
readinessState: "not_required",
|
|
64338
|
+
recoveryAction: "none"
|
|
64339
|
+
};
|
|
64340
|
+
}
|
|
64341
|
+
async function bindMessageJobProviderToCurrentDefault(deps, jobId, stage = "inbound-xmtp", options = {}) {
|
|
63054
64342
|
const currentProvider = deps.sessionStore?.getDefaultAiProvider() ?? null;
|
|
63055
64343
|
if (!jobId || jobId === BACKUP_JOB_ID) {
|
|
63056
|
-
return
|
|
64344
|
+
return neutralProviderGateResult(currentProvider);
|
|
63057
64345
|
}
|
|
63058
64346
|
try {
|
|
63059
|
-
const result =
|
|
64347
|
+
const result = await bindNewJobProviderIfReady({
|
|
63060
64348
|
store: deps.sessionStore,
|
|
63061
|
-
jobId
|
|
64349
|
+
jobId,
|
|
64350
|
+
env: options.env,
|
|
64351
|
+
platform: options.platform,
|
|
64352
|
+
readiness: options.readiness
|
|
63062
64353
|
});
|
|
63063
64354
|
if (!result) {
|
|
63064
|
-
return
|
|
64355
|
+
return neutralProviderGateResult(currentProvider);
|
|
63065
64356
|
}
|
|
63066
|
-
if (result
|
|
64357
|
+
if (result.degraded) {
|
|
64358
|
+
logWithTimestamp(
|
|
64359
|
+
`[okx-agent-task:${deps.myXmtpAddress}] job provider not ready at ${stage}: job=${shortenLogValue(jobId)} state=${result.readiness?.state ?? "not_required"} recovery=${result.readiness?.recoveryAction ?? "none"}; binding deferred, message still processed`
|
|
64360
|
+
);
|
|
64361
|
+
return {
|
|
64362
|
+
allowed: true,
|
|
64363
|
+
created: false,
|
|
64364
|
+
provider: null,
|
|
64365
|
+
currentProvider,
|
|
64366
|
+
degraded: true,
|
|
64367
|
+
bindingSource: null,
|
|
64368
|
+
readinessState: result.readiness?.state ?? "not_required",
|
|
64369
|
+
recoveryAction: result.readiness?.recoveryAction ?? "none"
|
|
64370
|
+
};
|
|
64371
|
+
}
|
|
64372
|
+
const boundProvider = result.provider ? normalizeAiProvider(result.provider) : null;
|
|
64373
|
+
if (result.created) {
|
|
63067
64374
|
logWithTimestamp(
|
|
63068
|
-
`[okx-agent-task:${deps.myXmtpAddress}] job provider bound from ${stage}: job=${shortenLogValue(jobId)} provider=${
|
|
64375
|
+
`[okx-agent-task:${deps.myXmtpAddress}] job provider bound from ${stage}: job=${shortenLogValue(jobId)} provider=${boundProvider}`
|
|
63069
64376
|
);
|
|
63070
64377
|
}
|
|
63071
|
-
if (currentProvider &&
|
|
64378
|
+
if (currentProvider && boundProvider !== currentProvider) {
|
|
63072
64379
|
logWithTimestamp(
|
|
63073
|
-
`[okx-agent-task:${deps.myXmtpAddress}] job provider owner differs from default at ${stage}: job=${shortenLogValue(jobId)} owner=${
|
|
64380
|
+
`[okx-agent-task:${deps.myXmtpAddress}] job provider owner differs from default at ${stage}: job=${shortenLogValue(jobId)} owner=${boundProvider} default=${currentProvider}; dispatch will use owner binding`
|
|
63074
64381
|
);
|
|
63075
64382
|
}
|
|
63076
64383
|
return {
|
|
63077
64384
|
allowed: true,
|
|
63078
64385
|
created: result.created,
|
|
63079
|
-
provider:
|
|
63080
|
-
currentProvider
|
|
64386
|
+
provider: boundProvider,
|
|
64387
|
+
currentProvider,
|
|
64388
|
+
degraded: false,
|
|
64389
|
+
bindingSource: result.bindingSource,
|
|
64390
|
+
readinessState: result.readiness?.state ?? "not_required",
|
|
64391
|
+
recoveryAction: result.readiness?.recoveryAction ?? "none"
|
|
63081
64392
|
};
|
|
63082
64393
|
} catch (err) {
|
|
63083
64394
|
logWithTimestamp(
|
|
63084
64395
|
`[okx-agent-task:${deps.myXmtpAddress}] job provider bind failed from ${stage}: job=${shortenLogValue(jobId)}`,
|
|
63085
64396
|
err
|
|
63086
64397
|
);
|
|
63087
|
-
return
|
|
64398
|
+
return neutralProviderGateResult(currentProvider);
|
|
63088
64399
|
}
|
|
63089
64400
|
}
|
|
63090
64401
|
function isCanonicalJobPeerSessionKey(sessionKey) {
|
|
@@ -63366,7 +64677,7 @@ async function dispatchDirectToUserNotification(params) {
|
|
|
63366
64677
|
...extra
|
|
63367
64678
|
});
|
|
63368
64679
|
const providerBindStartedAt = Date.now();
|
|
63369
|
-
const providerGate = bindMessageJobProviderToCurrentDefault(deps, notification.jobId, "direct-to-user");
|
|
64680
|
+
const providerGate = await bindMessageJobProviderToCurrentDefault(deps, notification.jobId, "direct-to-user");
|
|
63370
64681
|
timing.mark("providerBind", providerBindStartedAt);
|
|
63371
64682
|
logger.info(LogEvent.SYSTEM_NOTIFICATION_RECEIVED, commonExtras({
|
|
63372
64683
|
...inboundStageExtras("inbound/direct_to_user_received", "success"),
|
|
@@ -63880,7 +65191,7 @@ async function processFileMessage(ctx, deps, options = {}) {
|
|
|
63880
65191
|
return true;
|
|
63881
65192
|
}
|
|
63882
65193
|
const providerBindStartedAt = Date.now();
|
|
63883
|
-
const providerGate = bindMessageJobProviderToCurrentDefault(deps, systemNotification.jobId, "system-notification");
|
|
65194
|
+
const providerGate = await bindMessageJobProviderToCurrentDefault(deps, systemNotification.jobId, "system-notification");
|
|
63884
65195
|
timing.mark("providerBind", providerBindStartedAt);
|
|
63885
65196
|
if (!providerGate.allowed) {
|
|
63886
65197
|
logger.info(LogEvent.INBOUND_IGNORED_NON_BUSINESS, timing.extras({
|
|
@@ -64001,7 +65312,7 @@ async function processFileMessage(ctx, deps, options = {}) {
|
|
|
64001
65312
|
timing.mark("routeResolve", routeStartedAt);
|
|
64002
65313
|
if (route.route === "job") {
|
|
64003
65314
|
const providerBindStartedAt = Date.now();
|
|
64004
|
-
const providerGate = bindMessageJobProviderToCurrentDefault(deps, route.jobId, "inbound-xmtp");
|
|
65315
|
+
const providerGate = await bindMessageJobProviderToCurrentDefault(deps, route.jobId, "inbound-xmtp");
|
|
64005
65316
|
timing.mark("providerBind", providerBindStartedAt);
|
|
64006
65317
|
if (!providerGate.allowed) {
|
|
64007
65318
|
logger.info(LogEvent.INBOUND_IGNORED_NON_BUSINESS, timing.extras({
|
|
@@ -64920,12 +66231,12 @@ async function runListenerWithLock(options, paths) {
|
|
|
64920
66231
|
});
|
|
64921
66232
|
}
|
|
64922
66233
|
});
|
|
64923
|
-
service.setPluginVersion("0.2.
|
|
66234
|
+
service.setPluginVersion("0.2.3-beta-6f2a8ec2c7-260810184309");
|
|
64924
66235
|
await service.init();
|
|
64925
66236
|
const pluginVersionStatus = service.pluginVersionStatus;
|
|
64926
66237
|
if (pluginVersionStatus.unavailable) {
|
|
64927
66238
|
throw new Error(
|
|
64928
|
-
`@okxweb3/a2a-node v${"0.2.
|
|
66239
|
+
`@okxweb3/a2a-node v${"0.2.3-beta-6f2a8ec2c7-260810184309"} is below the required minimum v${pluginVersionStatus.minVersion}`
|
|
64929
66240
|
);
|
|
64930
66241
|
}
|
|
64931
66242
|
const systemConfig = service.getSystemConfig();
|
|
@@ -64943,7 +66254,7 @@ async function runListenerWithLock(options, paths) {
|
|
|
64943
66254
|
onchainosAgentId: "*",
|
|
64944
66255
|
reason: "system-config missing sentryDsn",
|
|
64945
66256
|
pluginId: "@okxweb3/a2a-node",
|
|
64946
|
-
pluginVersion: "0.2.
|
|
66257
|
+
pluginVersion: "0.2.3-beta-6f2a8ec2c7-260810184309"
|
|
64947
66258
|
});
|
|
64948
66259
|
}
|
|
64949
66260
|
logWithTimestamp(
|
|
@@ -66210,6 +67521,32 @@ function validTimestamp(value) {
|
|
|
66210
67521
|
init_session_store();
|
|
66211
67522
|
init_ai_provider();
|
|
66212
67523
|
init_ai_command();
|
|
67524
|
+
init_codex_readiness();
|
|
67525
|
+
|
|
67526
|
+
// src/codex-readiness-types.ts
|
|
67527
|
+
var CODEX_COMMAND_SOURCES = [
|
|
67528
|
+
"explicit_override",
|
|
67529
|
+
"persisted",
|
|
67530
|
+
"path",
|
|
67531
|
+
"app_bundled"
|
|
67532
|
+
];
|
|
67533
|
+
var CODEX_READINESS_STATES = [
|
|
67534
|
+
"ready",
|
|
67535
|
+
"not_installed",
|
|
67536
|
+
"not_authenticated",
|
|
67537
|
+
"probe_timeout",
|
|
67538
|
+
"probe_failed"
|
|
67539
|
+
];
|
|
67540
|
+
var CODEX_RECOVERY_ACTIONS = ["none", "run_setup", "run_login", "install_cli"];
|
|
67541
|
+
var JOB_PROVIDER_BINDING_SOURCES = [
|
|
67542
|
+
"existing_binding",
|
|
67543
|
+
"current_runtime",
|
|
67544
|
+
"default_provider",
|
|
67545
|
+
"explicit_env"
|
|
67546
|
+
];
|
|
67547
|
+
|
|
67548
|
+
// src/index.ts
|
|
67549
|
+
init_sentry_logger();
|
|
66213
67550
|
init_update_cli();
|
|
66214
67551
|
init_win_spawn();
|
|
66215
67552
|
|
|
@@ -67563,7 +68900,7 @@ async function exportDiagnosticLogs(options) {
|
|
|
67563
68900
|
node: process.version,
|
|
67564
68901
|
platform: process.platform,
|
|
67565
68902
|
arch: process.arch,
|
|
67566
|
-
packageVersion: true ? "0.2.
|
|
68903
|
+
packageVersion: true ? "0.2.3-beta-6f2a8ec2c7-260810184309" : "unknown",
|
|
67567
68904
|
sensitiveContentIncluded: options.includeSensitiveContent,
|
|
67568
68905
|
listenerAndLlmContentIncluded: true,
|
|
67569
68906
|
credentialsAlwaysRedacted: true,
|
|
@@ -68432,6 +69769,7 @@ init_log();
|
|
|
68432
69769
|
init_win_compat();
|
|
68433
69770
|
init_ai_command();
|
|
68434
69771
|
init_ai_provider();
|
|
69772
|
+
init_codex_readiness();
|
|
68435
69773
|
init_autostart();
|
|
68436
69774
|
init_autostart_windows();
|
|
68437
69775
|
init_daemon();
|
|
@@ -68465,11 +69803,70 @@ async function refreshAgentsAndWait(timeoutMs = 6e4) {
|
|
|
68465
69803
|
|
|
68466
69804
|
// src/runtime-switch.ts
|
|
68467
69805
|
init_ai_provider();
|
|
68468
|
-
|
|
68469
|
-
|
|
69806
|
+
init_sentry_logger();
|
|
69807
|
+
init_log_fields();
|
|
69808
|
+
var RUNTIME_SWITCH_COMPONENT = "node_runtime_switch";
|
|
69809
|
+
async function performRuntimeSwitch(store, options = {}) {
|
|
69810
|
+
const result = await switchProviderWithReadinessGate({
|
|
69811
|
+
store,
|
|
69812
|
+
env: options.env ?? process.env,
|
|
69813
|
+
platform: options.platform,
|
|
69814
|
+
readiness: options.readiness
|
|
69815
|
+
});
|
|
68470
69816
|
await dispatchRuntimeSwitchUserMessage(store, result);
|
|
68471
69817
|
return result;
|
|
68472
69818
|
}
|
|
69819
|
+
async function switchProviderWithReadinessGate(options) {
|
|
69820
|
+
const gated = await switchProviderWithReadinessGateCore(options);
|
|
69821
|
+
emitRuntimeSwitchCheckpoints(gated);
|
|
69822
|
+
return gated.result;
|
|
69823
|
+
}
|
|
69824
|
+
function emitRuntimeSwitchCheckpoints(gated) {
|
|
69825
|
+
const { result, runtime, readiness } = gated;
|
|
69826
|
+
logger.info(LogEvent.RUNTIME_DETECTED, {
|
|
69827
|
+
component: RUNTIME_SWITCH_COMPONENT,
|
|
69828
|
+
provider: runtime,
|
|
69829
|
+
detectedProvider: runtime,
|
|
69830
|
+
...logFieldExtras({
|
|
69831
|
+
checkpoint: "runtime_detected",
|
|
69832
|
+
outcome: runtime === "unknown" ? "ignored" : "success",
|
|
69833
|
+
transport: "cli",
|
|
69834
|
+
eventFamily: "lifecycle"
|
|
69835
|
+
})
|
|
69836
|
+
});
|
|
69837
|
+
if (readiness) {
|
|
69838
|
+
logger.info(LogEvent.PROVIDER_READINESS_CHECKED, {
|
|
69839
|
+
component: RUNTIME_SWITCH_COMPONENT,
|
|
69840
|
+
provider: readiness.provider,
|
|
69841
|
+
readinessState: readiness.state,
|
|
69842
|
+
commandSource: readiness.commandSource ?? "",
|
|
69843
|
+
commandPersisted: String(readiness.commandPersisted),
|
|
69844
|
+
recoveryAction: readiness.recoveryAction,
|
|
69845
|
+
...logFieldExtras({
|
|
69846
|
+
checkpoint: readiness.ready ? "provider_ready" : "provider_not_ready",
|
|
69847
|
+
outcome: readiness.ready ? "success" : "failed",
|
|
69848
|
+
transport: "cli",
|
|
69849
|
+
eventFamily: "lifecycle"
|
|
69850
|
+
})
|
|
69851
|
+
});
|
|
69852
|
+
}
|
|
69853
|
+
if (result.ok && result.action === "switched") {
|
|
69854
|
+
logger.info(LogEvent.PROVIDER_SWITCHED, {
|
|
69855
|
+
component: RUNTIME_SWITCH_COMPONENT,
|
|
69856
|
+
provider: result.provider,
|
|
69857
|
+
previousProvider: result.previousProvider ?? "",
|
|
69858
|
+
changed: String(result.changed),
|
|
69859
|
+
readinessState: result.readinessState ?? "not_required",
|
|
69860
|
+
commandSource: result.commandSource ?? "",
|
|
69861
|
+
...logFieldExtras({
|
|
69862
|
+
checkpoint: "provider_switched",
|
|
69863
|
+
outcome: "success",
|
|
69864
|
+
transport: "cli",
|
|
69865
|
+
eventFamily: "lifecycle"
|
|
69866
|
+
})
|
|
69867
|
+
});
|
|
69868
|
+
}
|
|
69869
|
+
}
|
|
68473
69870
|
async function dispatchRuntimeSwitchUserMessage(store, result) {
|
|
68474
69871
|
if (!result.ok || !result.changed || !result.userMessage) {
|
|
68475
69872
|
return;
|
|
@@ -68803,20 +70200,34 @@ var providerBindingChecker = {
|
|
|
68803
70200
|
}
|
|
68804
70201
|
}
|
|
68805
70202
|
};
|
|
70203
|
+
function resolveProviderCliTarget(env, store) {
|
|
70204
|
+
const runtime = detectRuntime(env);
|
|
70205
|
+
if (runtime === "codex" || runtime === "claude") {
|
|
70206
|
+
return runtime;
|
|
70207
|
+
}
|
|
70208
|
+
if (runtime !== "unknown") {
|
|
70209
|
+
return null;
|
|
70210
|
+
}
|
|
70211
|
+
const storedDefault = store.getDefaultAiProvider();
|
|
70212
|
+
return storedDefault === "codex" || storedDefault === "claude" ? storedDefault : null;
|
|
70213
|
+
}
|
|
70214
|
+
function readProviderCliTarget(env) {
|
|
70215
|
+
const store = new SessionStore();
|
|
70216
|
+
try {
|
|
70217
|
+
const provider = resolveProviderCliTarget(env, store);
|
|
70218
|
+
return {
|
|
70219
|
+
provider,
|
|
70220
|
+
storedCommand: provider ? store.getAiProviderCommand(provider) : null
|
|
70221
|
+
};
|
|
70222
|
+
} finally {
|
|
70223
|
+
store.close();
|
|
70224
|
+
}
|
|
70225
|
+
}
|
|
68806
70226
|
var providerCliChecker = {
|
|
68807
70227
|
id: "provider_cli",
|
|
68808
70228
|
title: "AI provider CLI",
|
|
68809
70229
|
run: async (ctx) => {
|
|
68810
|
-
const
|
|
68811
|
-
let provider;
|
|
68812
|
-
let storedCommand;
|
|
68813
|
-
try {
|
|
68814
|
-
provider = store.getDefaultAiProvider();
|
|
68815
|
-
provider = provider === "codex" || provider === "claude" ? provider : null;
|
|
68816
|
-
storedCommand = provider ? store.getAiProviderCommand(provider) : null;
|
|
68817
|
-
} finally {
|
|
68818
|
-
store.close();
|
|
68819
|
-
}
|
|
70230
|
+
const { provider, storedCommand } = readProviderCliTarget(ctx.env);
|
|
68820
70231
|
if (!provider) {
|
|
68821
70232
|
return null;
|
|
68822
70233
|
}
|
|
@@ -68839,12 +70250,29 @@ var providerCliChecker = {
|
|
|
68839
70250
|
}
|
|
68840
70251
|
const auth = provider === "codex" ? await checkCodexCliAuthStatus(resolved) : await checkClaudeCliAuthStatus(resolved);
|
|
68841
70252
|
if (auth.ok) {
|
|
70253
|
+
if (provider === "codex" && ctx.env.OKX_A2A_AI_CODEX_COMMAND && storedCommand !== resolved) {
|
|
70254
|
+
return {
|
|
70255
|
+
id: "provider_cli",
|
|
70256
|
+
title: "AI provider CLI",
|
|
70257
|
+
status: "fail",
|
|
70258
|
+
severity: "required",
|
|
70259
|
+
detail: "codex CLI is authenticated but its command is not persisted for the background daemon",
|
|
70260
|
+
fix: {
|
|
70261
|
+
kind: "auto",
|
|
70262
|
+
description: "Persist the validated Codex command for background daemon use."
|
|
70263
|
+
},
|
|
70264
|
+
data: { provider }
|
|
70265
|
+
};
|
|
70266
|
+
}
|
|
68842
70267
|
return {
|
|
68843
70268
|
id: "provider_cli",
|
|
68844
70269
|
title: "AI provider CLI",
|
|
68845
70270
|
status: "pass",
|
|
68846
70271
|
severity: "required",
|
|
68847
|
-
detail
|
|
70272
|
+
// `detail` is forwarded to Sentry (doctor-sentry.ts), so it carries no
|
|
70273
|
+
// resolved path or arguments. The exact command stays in `data`, which is
|
|
70274
|
+
// local report output only.
|
|
70275
|
+
detail: `${provider} CLI is installed and logged in`,
|
|
68848
70276
|
data: { provider, command: resolved }
|
|
68849
70277
|
};
|
|
68850
70278
|
}
|
|
@@ -68855,7 +70283,8 @@ var providerCliChecker = {
|
|
|
68855
70283
|
title: "AI provider CLI",
|
|
68856
70284
|
status: "fail",
|
|
68857
70285
|
severity: "required",
|
|
68858
|
-
|
|
70286
|
+
// No resolved path in `detail`: it is forwarded to Sentry.
|
|
70287
|
+
detail: `${provider} CLI is installed but not logged in (${auth.reason})`,
|
|
68859
70288
|
// Logging in needs a human (device/browser auth). In --non-interactive
|
|
68860
70289
|
// mode never launch it — degrade to a manual instruction so unattended
|
|
68861
70290
|
// callers (install scripts) cannot hang waiting on stdin.
|
|
@@ -68872,18 +70301,9 @@ var providerCliChecker = {
|
|
|
68872
70301
|
};
|
|
68873
70302
|
},
|
|
68874
70303
|
applyFix: async (ctx) => {
|
|
68875
|
-
const
|
|
68876
|
-
let provider;
|
|
68877
|
-
let storedCommand;
|
|
68878
|
-
try {
|
|
68879
|
-
provider = store.getDefaultAiProvider();
|
|
68880
|
-
provider = provider === "codex" || provider === "claude" ? provider : null;
|
|
68881
|
-
storedCommand = provider ? store.getAiProviderCommand(provider) : null;
|
|
68882
|
-
} finally {
|
|
68883
|
-
store.close();
|
|
68884
|
-
}
|
|
70304
|
+
const { provider, storedCommand } = readProviderCliTarget(ctx.env);
|
|
68885
70305
|
if (!provider) {
|
|
68886
|
-
throw new Error("no codex/claude provider bound");
|
|
70306
|
+
throw new Error("no codex/claude runtime detected and no codex/claude provider bound");
|
|
68887
70307
|
}
|
|
68888
70308
|
const resolveCli = () => provider === "codex" ? resolveCodexCommandPath(ctx.env, storedCommand, ctx.platform) : resolveCommandPath(resolveAiProviderCommand(provider, ctx.env, storedCommand, ctx.platform), ctx.env, ctx.platform);
|
|
68889
70309
|
let resolved = resolveCli();
|
|
@@ -68918,6 +70338,28 @@ var providerCliChecker = {
|
|
|
68918
70338
|
}
|
|
68919
70339
|
steps.push("completed interactive login");
|
|
68920
70340
|
}
|
|
70341
|
+
if (provider === "codex") {
|
|
70342
|
+
const store = new SessionStore();
|
|
70343
|
+
try {
|
|
70344
|
+
const readiness = await checkCodexReadiness({
|
|
70345
|
+
store,
|
|
70346
|
+
env: ctx.env,
|
|
70347
|
+
platform: ctx.platform,
|
|
70348
|
+
forceRefresh: true,
|
|
70349
|
+
persistOverrideCommand: true
|
|
70350
|
+
});
|
|
70351
|
+
if (!readiness.ready) {
|
|
70352
|
+
throw new Error(
|
|
70353
|
+
`codex readiness could not be confirmed (${readiness.state}); ${readiness.recoveryGuidance}`.trim()
|
|
70354
|
+
);
|
|
70355
|
+
}
|
|
70356
|
+
if (readiness.commandPersisted) {
|
|
70357
|
+
steps.push("persisted the validated codex command");
|
|
70358
|
+
}
|
|
70359
|
+
} finally {
|
|
70360
|
+
store.close();
|
|
70361
|
+
}
|
|
70362
|
+
}
|
|
68921
70363
|
return steps.length > 0 ? steps.join("; ") : "CLI already installed and logged in";
|
|
68922
70364
|
}
|
|
68923
70365
|
};
|
|
@@ -69274,8 +70716,14 @@ var CHECKERS = [
|
|
|
69274
70716
|
npmChecker,
|
|
69275
70717
|
cliVersionChecker,
|
|
69276
70718
|
windowsNativeLauncherChecker,
|
|
69277
|
-
|
|
70719
|
+
// provider_cli BEFORE provider_binding, deliberately: the intended runtime has
|
|
70720
|
+
// to be installed, authenticated and durably persisted before the binding fixer
|
|
70721
|
+
// attempts its readiness-gated runtime switch. In the other order the switch
|
|
70722
|
+
// fails on a not-ready Codex, preserves the stale default, and the CLI checker
|
|
70723
|
+
// then inspects that stale provider instead of the detected one — so Codex is
|
|
70724
|
+
// never repaired and the run never converges.
|
|
69278
70725
|
providerCliChecker,
|
|
70726
|
+
providerBindingChecker,
|
|
69279
70727
|
gatewayPluginChecker,
|
|
69280
70728
|
gatewayConfigChecker,
|
|
69281
70729
|
autostartChecker,
|
|
@@ -69288,7 +70736,7 @@ async function runDoctor(options = {}) {
|
|
|
69288
70736
|
platform: options.platform ?? process.platform,
|
|
69289
70737
|
env: options.env ?? process.env,
|
|
69290
70738
|
target: options.target ?? resolveDoctorTarget(options.env ?? process.env),
|
|
69291
|
-
cliVersion: options.cliVersion ?? (true ? "0.2.
|
|
70739
|
+
cliVersion: options.cliVersion ?? (true ? "0.2.3-beta-6f2a8ec2c7-260810184309" : "0.0.0"),
|
|
69292
70740
|
fixMode: options.fix === true,
|
|
69293
70741
|
nonInteractive: options.nonInteractive === true,
|
|
69294
70742
|
packageChanged: false,
|
|
@@ -69513,6 +70961,13 @@ init_autostart_windows();
|
|
|
69513
70961
|
AgentMessageDirection,
|
|
69514
70962
|
AiRunner,
|
|
69515
70963
|
BACKUP_JOB_ID,
|
|
70964
|
+
CODEX_APP_DIRS_ENV,
|
|
70965
|
+
CODEX_AUTH_PROBE_TIMEOUT_MS,
|
|
70966
|
+
CODEX_COMMAND_SOURCES,
|
|
70967
|
+
CODEX_PROBE_TIMEOUT_MS,
|
|
70968
|
+
CODEX_READINESS_CACHE_TTL_MS,
|
|
70969
|
+
CODEX_READINESS_STATES,
|
|
70970
|
+
CODEX_RECOVERY_ACTIONS,
|
|
69516
70971
|
CommandStore,
|
|
69517
70972
|
DEFAULT_AI_PERMISSION_PRESET,
|
|
69518
70973
|
DEFAULT_OFFLINE_REPLAY_INTERVAL_SEC,
|
|
@@ -69522,10 +70977,13 @@ init_autostart_windows();
|
|
|
69522
70977
|
HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS,
|
|
69523
70978
|
InboundReplayGate,
|
|
69524
70979
|
InvalidXmtpMessageStore,
|
|
70980
|
+
JOB_PROVIDER_BINDING_SOURCES,
|
|
70981
|
+
LogEvent,
|
|
69525
70982
|
MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG,
|
|
69526
70983
|
MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
|
|
69527
70984
|
NATIVE_LAUNCHER_EXE_NAME,
|
|
69528
70985
|
OPENCLAW_GATEWAY_ROUTE_GROUP_ID,
|
|
70986
|
+
ProviderNotReadyError,
|
|
69529
70987
|
SYSTEM_NOTIFICATION_SESSION_KEY,
|
|
69530
70988
|
SessionBusyTracker,
|
|
69531
70989
|
SessionStore,
|
|
@@ -69546,15 +71004,20 @@ init_autostart_windows();
|
|
|
69546
71004
|
activeDaemonPointerPath,
|
|
69547
71005
|
aiRunSentryExtra,
|
|
69548
71006
|
assertHermesSetupSupportedOnPlatform,
|
|
71007
|
+
authStatusFromReason,
|
|
69549
71008
|
bindJobProviderToCurrentDefaultIfMissing,
|
|
71009
|
+
bindJobProviderToCurrentDefaultIfReady,
|
|
69550
71010
|
bindJobProviderToCurrentRuntimeIfMissing,
|
|
71011
|
+
bindJobProviderToCurrentRuntimeIfReady,
|
|
69551
71012
|
bindMessageJobProviderToCurrentDefault,
|
|
71013
|
+
bindNewJobProviderIfReady,
|
|
69552
71014
|
bindOpenClawGatewayRouteFromEnv,
|
|
69553
71015
|
buildAgentMessageNotice,
|
|
69554
71016
|
buildAiAdapterCommand,
|
|
69555
71017
|
buildAiProviderEnv,
|
|
69556
71018
|
buildAutostartVbs,
|
|
69557
71019
|
buildBackupJobSessionKey,
|
|
71020
|
+
buildCodexRecoveryGuidance,
|
|
69558
71021
|
buildDispatchSessionKey,
|
|
69559
71022
|
buildDoctorSentryExtra,
|
|
69560
71023
|
buildExternalCommandInvocation,
|
|
@@ -69583,7 +71046,15 @@ init_autostart_windows();
|
|
|
69583
71046
|
callSessionsCreate,
|
|
69584
71047
|
callSessionsDelete,
|
|
69585
71048
|
callSessionsSend,
|
|
71049
|
+
checkClaudeCliAuthStatus,
|
|
71050
|
+
checkCodexCliAuthStatus,
|
|
71051
|
+
checkCodexReadiness,
|
|
69586
71052
|
claimActiveDaemon,
|
|
71053
|
+
classifyJobBindingSource,
|
|
71054
|
+
clearCodexReadinessCache,
|
|
71055
|
+
codexCommandResolvesWithoutOverride,
|
|
71056
|
+
codexReadinessCacheKey,
|
|
71057
|
+
collectCodexAppBundledCandidates,
|
|
69587
71058
|
collectCodexCommandCandidates,
|
|
69588
71059
|
commandExists,
|
|
69589
71060
|
createAiToolFailureTracker,
|
|
@@ -69599,6 +71070,7 @@ init_autostart_windows();
|
|
|
69599
71070
|
ensureDaemonReady,
|
|
69600
71071
|
ensureDefaultAiProvider,
|
|
69601
71072
|
ensureOpenClawOkxA2aPluginConfig,
|
|
71073
|
+
ensureProviderReadyForBinding,
|
|
69602
71074
|
ensureWindowsNativeLauncher,
|
|
69603
71075
|
evictPid,
|
|
69604
71076
|
exportDiagnosticLogs,
|
|
@@ -69626,6 +71098,7 @@ init_autostart_windows();
|
|
|
69626
71098
|
isDefaultTaskHome,
|
|
69627
71099
|
isGatewayAvailableForHeartbeat,
|
|
69628
71100
|
isHermesGatewayPluginEnabled,
|
|
71101
|
+
isInfoEventAllowlisted,
|
|
69629
71102
|
isOfflineReplayForTrigger,
|
|
69630
71103
|
isOpenClawA2aPluginAvailable,
|
|
69631
71104
|
isOpenClawGatewayAvailable,
|
|
@@ -69651,11 +71124,15 @@ init_autostart_windows();
|
|
|
69651
71124
|
parsePluginYamlVersion,
|
|
69652
71125
|
parseWindowsParentProcessJson,
|
|
69653
71126
|
performRuntimeSwitch,
|
|
71127
|
+
persistCurrentRuntimeSelectionForNewJob,
|
|
69654
71128
|
pickOnchainosWin32Candidate,
|
|
71129
|
+
prepareAndBindCurrentRuntimeForNewJob,
|
|
69655
71130
|
printLogsExportUsage,
|
|
69656
71131
|
printXmtpTestUsage,
|
|
71132
|
+
probeCodexAuthStatus,
|
|
69657
71133
|
processFileMessage,
|
|
69658
71134
|
readAiProviderTimeoutMs,
|
|
71135
|
+
readCachedCodexReadiness,
|
|
69659
71136
|
readLastLines,
|
|
69660
71137
|
readParentProcessCommandForPlatform,
|
|
69661
71138
|
readUserAttentionWatcherEvent,
|
|
@@ -69663,14 +71140,19 @@ init_autostart_windows();
|
|
|
69663
71140
|
registerUserAttentionWatcher,
|
|
69664
71141
|
releaseActiveDaemon,
|
|
69665
71142
|
removeUserAttentionWatcher,
|
|
71143
|
+
replayScanBackoffMs,
|
|
69666
71144
|
reportDoctorRunToSentry,
|
|
69667
71145
|
reportUserNotificationFailure,
|
|
69668
71146
|
resetMessageEligibleOfflineReplayCapabilityForTests,
|
|
69669
71147
|
resetResolvedOnchainosBinForTests,
|
|
69670
71148
|
resolveAiPermissionPreset,
|
|
69671
71149
|
resolveAiProviderCommand,
|
|
71150
|
+
resolveAiProviderCommandWithSource,
|
|
69672
71151
|
resolveAiProviderForDispatch,
|
|
71152
|
+
resolveAiProviderForDispatchWithReadiness,
|
|
71153
|
+
resolveCodexCommandCandidates,
|
|
69673
71154
|
resolveCodexCommandPath,
|
|
71155
|
+
resolveCodexCommandSource,
|
|
69674
71156
|
resolveConfiguredAiProvider,
|
|
69675
71157
|
resolveConfiguredAiProviderForJob,
|
|
69676
71158
|
resolveDirectCommunicationSessionTarget,
|
|
@@ -69682,6 +71164,7 @@ init_autostart_windows();
|
|
|
69682
71164
|
resolveOpenClawGatewayConfig,
|
|
69683
71165
|
resolveOpenClawGatewayRoute,
|
|
69684
71166
|
resolveOpenClawGatewayRoutes,
|
|
71167
|
+
resolveProviderCommandWithSelfHeal,
|
|
69685
71168
|
resolveSystemNotificationTargets,
|
|
69686
71169
|
resolveTaskConfigPath,
|
|
69687
71170
|
resolveTaskHome,
|
|
@@ -69709,6 +71192,8 @@ init_autostart_windows();
|
|
|
69709
71192
|
subscribeUserAttentionEvents,
|
|
69710
71193
|
summarizeXmtpTestEvents,
|
|
69711
71194
|
switchProvider,
|
|
71195
|
+
switchProviderWithReadinessGate,
|
|
71196
|
+
switchProviderWithReadinessGateCore,
|
|
69712
71197
|
systemdUnitHasCurrentRestartPolicy,
|
|
69713
71198
|
toOpenClawGatewaySessionKey,
|
|
69714
71199
|
toWindowsInvocation,
|