karajan-code 3.13.0 → 3.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karajan-code",
3
- "version": "3.13.0",
3
+ "version": "3.13.1",
4
4
  "description": "Local multi-agent coding orchestrator with TDD, SonarQube, and code review pipeline",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0",
@@ -55,7 +55,10 @@ const DEFAULTS = {
55
55
  review_mode: "standard",
56
56
  max_iterations: 5,
57
57
  hu_max_iterations: 3,
58
- max_budget_usd: null,
58
+ // Hard per-run spend ceiling, ON by default (KJC-TSK-0621): a stuck or
59
+ // runaway run must never drain a subscription quota unattended. Explicit
60
+ // null in the user's config opts out (no cap).
61
+ max_budget_usd: 5,
59
62
  review_rules: "./.karajan/review-rules.md",
60
63
  coder_rules: "./.karajan/coder-rules.md",
61
64
  base_branch: "main",
@@ -172,8 +172,14 @@ export function applyRunOverrides(config, flags) {
172
172
  out.git = out.git || {};
173
173
  out.development = out.development || {};
174
174
  out.sonarqube = out.sonarqube || {};
175
- if (out.max_budget_usd === undefined || out.max_budget_usd === null) {
176
- out.max_budget_usd = out.session.max_budget_usd ?? null;
175
+ // Precedence: explicit top-level value (including null = opt-out) wins;
176
+ // the legacy session.max_budget_usd location next; the shipped default
177
+ // (KJC-TSK-0621: 5) last. When top-level still carries the default, a
178
+ // session-level value — including an explicit null — takes over.
179
+ if (out.max_budget_usd === undefined) {
180
+ out.max_budget_usd = out.session.max_budget_usd !== undefined ? out.session.max_budget_usd : DEFAULTS.max_budget_usd;
181
+ } else if (out.max_budget_usd === DEFAULTS.max_budget_usd && out.session.max_budget_usd !== undefined) {
182
+ out.max_budget_usd = out.session.max_budget_usd;
177
183
  }
178
184
  out.budget = mergeDeep(DEFAULTS.budget, out.budget || {});
179
185
  out.roles = mergeDeep(DEFAULTS.roles, out.roles || {});
@@ -153,7 +153,11 @@ export async function checkBudgetExceeded({ budgetTracker, config, session, emit
153
153
 
154
154
  await markSessionStatus(session, "failed");
155
155
  const totalCost = budgetTracker.total().cost_usd;
156
- const message = `Budget exceeded: $${totalCost.toFixed(2)} > $${budgetLimit.toFixed(2)}`;
156
+ const limit = Number(budgetLimit ?? config?.max_budget_usd);
157
+ const message =
158
+ `Budget exceeded: $${totalCost.toFixed(2)} > $${limit.toFixed(2)}. ` +
159
+ `Continue consciously with \`kj resume ${session.id}\`, or raise the cap ` +
160
+ `(max_budget_usd in .karajan/kj.config.yml; null removes it).`;
157
161
  emitProgress(
158
162
  emitter,
159
163
  makeEvent("session:end", { ...eventBase, iteration: i, stage: "budget" }, {
@@ -114,5 +114,7 @@ output:
114
114
  session:
115
115
  max_iteration_minutes: 15
116
116
  max_total_minutes: 120
117
- max_budget_usd: null
117
+ # Hard per-run spend ceiling (USD-equivalent). The run stops with a summary
118
+ # when exceeded; resume with `kj resume`. Set to null to remove the cap.
119
+ max_budget_usd: 5
118
120
  fail_fast_repeats: 2