@elisym/cli 0.17.2 → 0.18.0
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/index.js +85 -28
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -27,6 +27,14 @@ var __export = (target, all) => {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
// src/llm/providers/http.ts
|
|
30
|
+
function resolveLlmTimeoutMs() {
|
|
31
|
+
const raw = process.env.ELISYM_LLM_TIMEOUT_MS;
|
|
32
|
+
if (raw === void 0) {
|
|
33
|
+
return DEFAULT_LLM_TIMEOUT_MS;
|
|
34
|
+
}
|
|
35
|
+
const parsed = Number(raw);
|
|
36
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : DEFAULT_LLM_TIMEOUT_MS;
|
|
37
|
+
}
|
|
30
38
|
function createAbortError() {
|
|
31
39
|
const err = new Error("The operation was aborted");
|
|
32
40
|
err.name = "AbortError";
|
|
@@ -92,10 +100,11 @@ async function fetchWithRetry(url, init, signal) {
|
|
|
92
100
|
await sleepWithSignal(delay, signal);
|
|
93
101
|
}
|
|
94
102
|
}
|
|
95
|
-
var LLM_TIMEOUT_MS, MAX_RETRIES, RETRYABLE_STATUSES;
|
|
103
|
+
var DEFAULT_LLM_TIMEOUT_MS, LLM_TIMEOUT_MS, MAX_RETRIES, RETRYABLE_STATUSES;
|
|
96
104
|
var init_http = __esm({
|
|
97
105
|
"src/llm/providers/http.ts"() {
|
|
98
|
-
|
|
106
|
+
DEFAULT_LLM_TIMEOUT_MS = 6e5;
|
|
107
|
+
LLM_TIMEOUT_MS = resolveLlmTimeoutMs();
|
|
99
108
|
MAX_RETRIES = 2;
|
|
100
109
|
RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
101
110
|
}
|
|
@@ -1977,7 +1986,6 @@ function createLogger(options = {}) {
|
|
|
1977
1986
|
var payment = new SolanaPaymentStrategy();
|
|
1978
1987
|
var LEDGER_GC_INTERVAL_MS = 60 * 60 * 1e3;
|
|
1979
1988
|
var LEDGER_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
1980
|
-
var TOTAL_JOB_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
1981
1989
|
var MAX_PAID_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
1982
1990
|
var SIG_PATH_TIMEOUT_MS = 60 * 1e3;
|
|
1983
1991
|
function resolveJobPrice(tags, skills) {
|
|
@@ -2012,6 +2020,12 @@ var AgentUnavailableError = class extends Error {
|
|
|
2012
2020
|
this.name = "AgentUnavailableError";
|
|
2013
2021
|
}
|
|
2014
2022
|
};
|
|
2023
|
+
var ExecutionBudgetExceededError = class extends Error {
|
|
2024
|
+
constructor(budgetMs) {
|
|
2025
|
+
super(`Execution exceeded budget (${Math.round(budgetMs / 1e3)}s)`);
|
|
2026
|
+
this.name = "ExecutionBudgetExceededError";
|
|
2027
|
+
}
|
|
2028
|
+
};
|
|
2015
2029
|
function bodyLooksLikeBilling3(body) {
|
|
2016
2030
|
const lower = body.toLowerCase();
|
|
2017
2031
|
return BILLING_BODY_MARKERS3.some((marker) => lower.includes(marker));
|
|
@@ -2366,21 +2380,26 @@ var AgentRuntime = class {
|
|
|
2366
2380
|
}
|
|
2367
2381
|
this.transport.stop();
|
|
2368
2382
|
}
|
|
2369
|
-
/**
|
|
2383
|
+
/**
|
|
2384
|
+
* Resolve a job's execution budget in milliseconds. Per-skill
|
|
2385
|
+
* `executionTimeoutSecs` wins over the agent-level `config.executionTimeoutSecs`;
|
|
2386
|
+
* `0` (explicit unlimited) and `undefined` both collapse to `0` => no timer.
|
|
2387
|
+
*/
|
|
2388
|
+
resolveExecutionBudgetMs(skill) {
|
|
2389
|
+
const secs = skill.executionTimeoutSecs ?? this.config.executionTimeoutSecs ?? 0;
|
|
2390
|
+
return secs > 0 ? secs * 1e3 : 0;
|
|
2391
|
+
}
|
|
2392
|
+
/**
|
|
2393
|
+
* Wrapper with error handling. The execution budget (if any) is enforced
|
|
2394
|
+
* around `skill.execute` inside `executeJob`, not here - payment collection
|
|
2395
|
+
* and result delivery run on their own bounds. `jobAbort` is retained so
|
|
2396
|
+
* `stop()` can still abort an in-flight job.
|
|
2397
|
+
*/
|
|
2370
2398
|
async processJob(job) {
|
|
2371
|
-
let timeoutId;
|
|
2372
2399
|
const jobAbort = new AbortController();
|
|
2373
2400
|
this.jobAbortControllers.add(jobAbort);
|
|
2374
2401
|
try {
|
|
2375
|
-
await
|
|
2376
|
-
this.executeJob(job, jobAbort.signal),
|
|
2377
|
-
new Promise((_, reject) => {
|
|
2378
|
-
timeoutId = setTimeout(() => {
|
|
2379
|
-
jobAbort.abort();
|
|
2380
|
-
reject(new Error("Job processing timeout"));
|
|
2381
|
-
}, TOTAL_JOB_TIMEOUT_MS);
|
|
2382
|
-
})
|
|
2383
|
-
]);
|
|
2402
|
+
await this.executeJob(job, jobAbort.signal);
|
|
2384
2403
|
} catch (e) {
|
|
2385
2404
|
const log = this.callbacks.onLog ?? console.log;
|
|
2386
2405
|
log(`[${job.jobId.slice(0, 8)}] Error: ${e.message}`);
|
|
@@ -2406,7 +2425,6 @@ var AgentRuntime = class {
|
|
|
2406
2425
|
await this.transport.sendFeedback(job, { type: "error", message: safeMessage }).catch(() => {
|
|
2407
2426
|
});
|
|
2408
2427
|
} finally {
|
|
2409
|
-
clearTimeout(timeoutId);
|
|
2410
2428
|
this.jobAbortControllers.delete(jobAbort);
|
|
2411
2429
|
}
|
|
2412
2430
|
}
|
|
@@ -2468,6 +2486,21 @@ var AgentRuntime = class {
|
|
|
2468
2486
|
}
|
|
2469
2487
|
log(`[${job.jobId.slice(0, 8)}] Executing skill: ${skill.name}`);
|
|
2470
2488
|
let output;
|
|
2489
|
+
const budgetMs = this.resolveExecutionBudgetMs(skill);
|
|
2490
|
+
let budgetExceeded = false;
|
|
2491
|
+
const execAbort = new AbortController();
|
|
2492
|
+
const onOuterAbort = () => execAbort.abort();
|
|
2493
|
+
if (signal) {
|
|
2494
|
+
if (signal.aborted) {
|
|
2495
|
+
execAbort.abort();
|
|
2496
|
+
} else {
|
|
2497
|
+
signal.addEventListener("abort", onOuterAbort);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
const budgetTimer = budgetMs > 0 ? setTimeout(() => {
|
|
2501
|
+
budgetExceeded = true;
|
|
2502
|
+
execAbort.abort();
|
|
2503
|
+
}, budgetMs) : void 0;
|
|
2471
2504
|
try {
|
|
2472
2505
|
output = await skill.execute(
|
|
2473
2506
|
{
|
|
@@ -2476,14 +2509,25 @@ var AgentRuntime = class {
|
|
|
2476
2509
|
tags: job.tags,
|
|
2477
2510
|
jobId: job.jobId
|
|
2478
2511
|
},
|
|
2479
|
-
{ ...this.skillCtx, signal }
|
|
2512
|
+
{ ...this.skillCtx, signal: execAbort.signal }
|
|
2480
2513
|
);
|
|
2481
2514
|
} catch (err) {
|
|
2515
|
+
if (budgetExceeded) {
|
|
2516
|
+
log(`[${job.jobId.slice(0, 8)}] Execution exceeded budget (${budgetMs / 1e3}s)`);
|
|
2517
|
+
throw new ExecutionBudgetExceededError(budgetMs);
|
|
2518
|
+
}
|
|
2482
2519
|
const flippedToUnhealthy = this.markHealthFromExecuteError(skill, err, log, job.jobId);
|
|
2483
2520
|
if (flippedToUnhealthy) {
|
|
2484
2521
|
throw new AgentUnavailableError();
|
|
2485
2522
|
}
|
|
2486
2523
|
throw err;
|
|
2524
|
+
} finally {
|
|
2525
|
+
if (budgetTimer) {
|
|
2526
|
+
clearTimeout(budgetTimer);
|
|
2527
|
+
}
|
|
2528
|
+
if (signal) {
|
|
2529
|
+
signal.removeEventListener("abort", onOuterAbort);
|
|
2530
|
+
}
|
|
2487
2531
|
}
|
|
2488
2532
|
this.ledger.markExecuted(job.jobId, output.data);
|
|
2489
2533
|
log(`[${job.jobId.slice(0, 8)}] Skill completed, delivering result`);
|
|
@@ -2731,7 +2775,6 @@ var AgentRuntime = class {
|
|
|
2731
2775
|
async recoverSingleJob(entry, log) {
|
|
2732
2776
|
const recoveryAbort = new AbortController();
|
|
2733
2777
|
this.jobAbortControllers.add(recoveryAbort);
|
|
2734
|
-
const timeout = setTimeout(() => recoveryAbort.abort(), TOTAL_JOB_TIMEOUT_MS);
|
|
2735
2778
|
try {
|
|
2736
2779
|
const rawEvent = JSON.parse(entry.raw_event_json);
|
|
2737
2780
|
const fakeJob = {
|
|
@@ -2788,22 +2831,30 @@ var AgentRuntime = class {
|
|
|
2788
2831
|
return;
|
|
2789
2832
|
}
|
|
2790
2833
|
}
|
|
2791
|
-
const
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2834
|
+
const recoveryBudgetMs = this.resolveExecutionBudgetMs(skill);
|
|
2835
|
+
const budgetTimer = recoveryBudgetMs > 0 ? setTimeout(() => recoveryAbort.abort(), recoveryBudgetMs) : void 0;
|
|
2836
|
+
let output;
|
|
2837
|
+
try {
|
|
2838
|
+
output = await skill.execute(
|
|
2839
|
+
{
|
|
2840
|
+
data: entry.input,
|
|
2841
|
+
inputType: entry.input_type,
|
|
2842
|
+
tags: entry.tags,
|
|
2843
|
+
jobId: entry.job_id
|
|
2844
|
+
},
|
|
2845
|
+
{ ...this.skillCtx, signal: recoveryAbort.signal }
|
|
2846
|
+
);
|
|
2847
|
+
} finally {
|
|
2848
|
+
if (budgetTimer) {
|
|
2849
|
+
clearTimeout(budgetTimer);
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2800
2852
|
this.ledger.markExecuted(entry.job_id, output.data);
|
|
2801
2853
|
await this.transport.deliverResult(fakeJob, output.data, entry.net_amount);
|
|
2802
2854
|
this.ledger.markDelivered(entry.job_id);
|
|
2803
2855
|
log(`[${entry.job_id.slice(0, 8)}] Recovery: re-executed and delivered`);
|
|
2804
2856
|
}
|
|
2805
2857
|
} finally {
|
|
2806
|
-
clearTimeout(timeout);
|
|
2807
2858
|
this.jobAbortControllers.delete(recoveryAbort);
|
|
2808
2859
|
}
|
|
2809
2860
|
}
|
|
@@ -3163,6 +3214,9 @@ function buildCliSkill(parsed, entryPath, scriptEnv) {
|
|
|
3163
3214
|
if (parsed.rateLimit) {
|
|
3164
3215
|
skill.rateLimit = parsed.rateLimit;
|
|
3165
3216
|
}
|
|
3217
|
+
if (parsed.executionTimeoutSecs !== void 0) {
|
|
3218
|
+
skill.executionTimeoutSecs = parsed.executionTimeoutSecs;
|
|
3219
|
+
}
|
|
3166
3220
|
return skill;
|
|
3167
3221
|
}
|
|
3168
3222
|
function loadSkillsFromDir(skillsDir, options = {}) {
|
|
@@ -3940,7 +3994,10 @@ async function cmdStart(nameArg, options = {}) {
|
|
|
3940
3994
|
recoveryMaxRetries: RECOVERY_MAX_RETRIES,
|
|
3941
3995
|
recoveryIntervalSecs: RECOVERY_INTERVAL_SECS,
|
|
3942
3996
|
network: walletNetwork,
|
|
3943
|
-
solanaAddress
|
|
3997
|
+
solanaAddress,
|
|
3998
|
+
// Agent-level default execution budget; per-skill `max_execution_secs`
|
|
3999
|
+
// overrides it. Undefined => unlimited (operator-owned, no protocol default).
|
|
4000
|
+
executionTimeoutSecs: loaded.yaml.execution_timeout_secs
|
|
3944
4001
|
};
|
|
3945
4002
|
const rpcUrlForLog = stripRpcSecrets(process.env.SOLANA_RPC_URL ?? getRpcUrl());
|
|
3946
4003
|
logger.debug(
|