agentxchain 2.60.0 → 2.61.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/package.json
CHANGED
|
@@ -575,7 +575,7 @@ function attachLegacyCurrentTurnAlias(state) {
|
|
|
575
575
|
function formatBudgetRecoveryAction(isReadyToResume) {
|
|
576
576
|
return isReadyToResume
|
|
577
577
|
? 'Run agentxchain resume to assign the next turn'
|
|
578
|
-
: 'Increase
|
|
578
|
+
: 'Increase budget with agentxchain config --set budget.per_run_max_usd <usd>, then run agentxchain resume';
|
|
579
579
|
}
|
|
580
580
|
|
|
581
581
|
function formatBudgetRecoveryDetail(spentUsd, limitUsd, remainingUsd, isReadyToResume) {
|
|
@@ -1938,7 +1938,7 @@ export function assignGovernedTurn(root, config, roleId) {
|
|
|
1938
1938
|
|
|
1939
1939
|
// DEC-BUDGET-ENFORCE-001: Pre-assignment budget exhaustion guard
|
|
1940
1940
|
if (state.budget_status?.remaining_usd != null && state.budget_status.remaining_usd <= 0) {
|
|
1941
|
-
return { ok: false, error: `Cannot assign turn: run budget exhausted (spent $${(state.budget_status.spent_usd || 0).toFixed(2)} of $${((state.budget_status.spent_usd || 0) + state.budget_status.remaining_usd).toFixed(2)} limit). Increase
|
|
1941
|
+
return { ok: false, error: `Cannot assign turn: run budget exhausted (spent $${(state.budget_status.spent_usd || 0).toFixed(2)} of $${((state.budget_status.spent_usd || 0) + state.budget_status.remaining_usd).toFixed(2)} limit). Increase budget with agentxchain config --set budget.per_run_max_usd <usd>, then run agentxchain resume` };
|
|
1942
1942
|
}
|
|
1943
1943
|
|
|
1944
1944
|
// DEC-PARALLEL-011: Budget reservation
|
|
@@ -2675,7 +2675,7 @@ function _acceptGovernedTurnLocked(root, config, opts) {
|
|
|
2675
2675
|
recovery: {
|
|
2676
2676
|
typed_reason: 'budget_exhausted',
|
|
2677
2677
|
owner: 'human',
|
|
2678
|
-
recovery_action: 'Increase
|
|
2678
|
+
recovery_action: 'Increase budget with agentxchain config --set budget.per_run_max_usd <usd>, then run agentxchain resume',
|
|
2679
2679
|
turn_retained: false,
|
|
2680
2680
|
detail: `Run budget exhausted: spent $${updatedState.budget_status.spent_usd.toFixed(2)} of $${limit.toFixed(2)} limit ($${overBy.toFixed(2)} over)`,
|
|
2681
2681
|
},
|
|
@@ -61,6 +61,7 @@ const VALID_API_PROXY_PREFLIGHT_FIELDS = [
|
|
|
61
61
|
'tokenizer',
|
|
62
62
|
'safety_margin_tokens',
|
|
63
63
|
];
|
|
64
|
+
const VALID_BUDGET_ON_EXCEED = ['pause_and_escalate'];
|
|
64
65
|
|
|
65
66
|
function validateMcpRuntime(runtimeId, runtime, errors) {
|
|
66
67
|
const transport = typeof runtime?.transport === 'string' && runtime.transport.trim()
|
|
@@ -537,6 +538,12 @@ export function validateV4Config(data, projectRoot) {
|
|
|
537
538
|
errors.push(...policyValidation.errors);
|
|
538
539
|
}
|
|
539
540
|
|
|
541
|
+
// Budget (optional but validated if present)
|
|
542
|
+
if (data.budget !== undefined) {
|
|
543
|
+
const budgetValidation = validateBudgetConfig(data.budget);
|
|
544
|
+
errors.push(...budgetValidation.errors);
|
|
545
|
+
}
|
|
546
|
+
|
|
540
547
|
// Approval Policy (optional but validated if present)
|
|
541
548
|
if (data.approval_policy !== undefined) {
|
|
542
549
|
errors.push(...validateApprovalPolicy(data.approval_policy, data.routing));
|
|
@@ -551,6 +558,80 @@ export function validateV4Config(data, projectRoot) {
|
|
|
551
558
|
return { ok: errors.length === 0, errors };
|
|
552
559
|
}
|
|
553
560
|
|
|
561
|
+
export function validateBudgetConfig(budget) {
|
|
562
|
+
const errors = [];
|
|
563
|
+
|
|
564
|
+
if (budget === null) {
|
|
565
|
+
return { ok: true, errors };
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (!budget || typeof budget !== 'object' || Array.isArray(budget)) {
|
|
569
|
+
errors.push('budget must be an object');
|
|
570
|
+
return { ok: false, errors };
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
validateBudgetUsdLimit('budget.per_turn_max_usd', budget.per_turn_max_usd, errors);
|
|
574
|
+
validateBudgetUsdLimit('budget.per_run_max_usd', budget.per_run_max_usd, errors);
|
|
575
|
+
|
|
576
|
+
if (
|
|
577
|
+
Number.isFinite(budget.per_turn_max_usd) &&
|
|
578
|
+
Number.isFinite(budget.per_run_max_usd) &&
|
|
579
|
+
budget.per_turn_max_usd > budget.per_run_max_usd
|
|
580
|
+
) {
|
|
581
|
+
errors.push('budget.per_turn_max_usd must be less than or equal to budget.per_run_max_usd when both are set');
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if (budget.on_exceed !== undefined) {
|
|
585
|
+
if (typeof budget.on_exceed !== 'string' || !VALID_BUDGET_ON_EXCEED.includes(budget.on_exceed)) {
|
|
586
|
+
errors.push(`budget.on_exceed must be one of: ${VALID_BUDGET_ON_EXCEED.join(', ')} (warn is not implemented)`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (budget.cost_rates !== undefined) {
|
|
591
|
+
if (!budget.cost_rates || typeof budget.cost_rates !== 'object' || Array.isArray(budget.cost_rates)) {
|
|
592
|
+
errors.push('budget.cost_rates must be an object');
|
|
593
|
+
} else {
|
|
594
|
+
for (const [model, rates] of Object.entries(budget.cost_rates)) {
|
|
595
|
+
if (typeof model !== 'string' || !model.trim()) {
|
|
596
|
+
errors.push('budget.cost_rates model keys must be non-empty strings');
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
if (!rates || typeof rates !== 'object' || Array.isArray(rates)) {
|
|
600
|
+
errors.push(`budget.cost_rates.${model} must be an object`);
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
validateBudgetCostRate(`budget.cost_rates.${model}.input_per_1m`, rates.input_per_1m, errors);
|
|
604
|
+
validateBudgetCostRate(`budget.cost_rates.${model}.output_per_1m`, rates.output_per_1m, errors);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
return { ok: errors.length === 0, errors };
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function validateBudgetUsdLimit(path, value, errors) {
|
|
613
|
+
if (value === undefined || value === null) {
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
617
|
+
errors.push(`${path} must be a finite number when provided`);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
if (value <= 0) {
|
|
621
|
+
errors.push(`${path} must be greater than 0 when provided`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function validateBudgetCostRate(path, value, errors) {
|
|
626
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
627
|
+
errors.push(`${path} must be a finite number`);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
if (value < 0) {
|
|
631
|
+
errors.push(`${path} must be greater than or equal to 0`);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
554
635
|
export function validateSchedulesConfig(schedules, roles) {
|
|
555
636
|
const errors = [];
|
|
556
637
|
|