clementine-agent 1.18.185 → 1.18.186

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.
@@ -755,15 +755,35 @@ export async function runAgent(prompt, opts) {
755
755
  }
756
756
  catch { /* non-fatal */ }
757
757
  // Translate the SDK's budget-exhaustion throw into a message that
758
- // tells the user (a) what cap tripped and (b) how to raise it.
758
+ // tells the user (a) what cap tripped and (b) how to actually raise it.
759
759
  // The raw SDK string ("Claude Code returned an error result:
760
760
  // Reached maximum budget ($0.5)") leaks through the channel layer
761
761
  // as a generic "Something went wrong:" with no actionable hint.
762
+ //
763
+ // 1.18.186: name the REAL source of the cap. Previously we suggested
764
+ // a synthesized env-var name (e.g., "BUDGET_SCHEDULED_SKILL_USD")
765
+ // that didn't exist anywhere in the code — confusing users who set
766
+ // that key and saw nothing change. Real sources, in resolution order:
767
+ // - explicit options.maxBudgetUsd from the caller
768
+ // - skill.frontmatter.clementine.limits.maxBudgetUsd (per-skill)
769
+ // - BUDGET.cronT1 / cronT2 / heartbeat / chat (global env / dashboard)
770
+ // - DEFAULT_BUDGETS[source] fallback
762
771
  if (/Reached maximum budget|error_max_budget_usd/i.test(errMsg)) {
763
772
  const cap = maxBudgetUsd?.toFixed(2) ?? '?';
764
- const envKey = `BUDGET_${source.toUpperCase().replace(/-/g, '_')}_USD`;
765
- throw new Error(`Hit the $${cap} ${source} budget cap before finishing. ` +
766
- `Raise it in the dashboard (Budgets & Costs) or set ${envKey}=0 to remove caps.`);
773
+ // Recognized global env keys — these are real. The rest of the
774
+ // surface (skill frontmatter, options.maxBudgetUsd) is per-call.
775
+ const globalKey = (() => {
776
+ switch (source) {
777
+ case 'cron': return 'BUDGET_CRON_T1_USD or BUDGET_CRON_T2_USD (whichever tier this job uses)';
778
+ case 'heartbeat': return 'BUDGET_HEARTBEAT_USD';
779
+ case 'chat': return 'BUDGET_CHAT_USD';
780
+ default: return null;
781
+ }
782
+ })();
783
+ const remediation = globalKey
784
+ ? `Raise it in the dashboard (Budgets & Costs), set ${globalKey}=0 to remove the global cap, OR check the skill's frontmatter \`clementine.limits.maxBudgetUsd\` if the cap came from there.`
785
+ : `This is a per-call or per-skill cap. Check the caller's \`maxBudgetUsd\` option OR the skill's frontmatter \`clementine.limits.maxBudgetUsd\` field.`;
786
+ throw new Error(`Hit the $${cap} ${source} budget cap before finishing. ${remediation}`);
767
787
  }
768
788
  throw err;
769
789
  }
@@ -362,7 +362,21 @@ export async function runSkill(name, options = {}) {
362
362
  : renderedSkillPrompt;
363
363
  const limits = skill.frontmatter?.clementine?.limits;
364
364
  const maxTurns = options.maxTurns ?? limits?.maxTurns;
365
- const maxBudgetUsd = options.maxBudgetUsd ?? limits?.maxBudgetUsd;
365
+ // 1.18.186 dashboard is the boss on budget. If the user has
366
+ // explicitly set ALL global BUDGET_*_USD to 0 ("no budget anywhere"),
367
+ // per-skill frontmatter caps should yield. Otherwise the dashboard
368
+ // shows "no budget" while skills still silently hit their own caps,
369
+ // which is exactly the surprise diagnosed on 2026-05-11.
370
+ //
371
+ // The check looks at the four published global budgets — if every
372
+ // one of them is 0 (uncapped), we treat the install as "no caps mode"
373
+ // and ignore frontmatter caps too. Caller-supplied options.maxBudgetUsd
374
+ // always wins (explicit > implicit).
375
+ const { BUDGET } = await import('../config.js');
376
+ const globalCapsAllZero = BUDGET.heartbeat === 0 && BUDGET.cronT1 === 0 &&
377
+ BUDGET.cronT2 === 0 && BUDGET.chat === 0;
378
+ const maxBudgetUsd = options.maxBudgetUsd
379
+ ?? (globalCapsAllZero ? undefined : limits?.maxBudgetUsd);
366
380
  const sessionKey = options.sessionKey
367
381
  ?? `skill:${name}:${Date.now().toString(36)}`;
368
382
  // Surface the skill folder to the SDK via additionalDirectories so
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.18.185",
3
+ "version": "1.18.186",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",