clementine-agent 1.0.89 → 1.0.90

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.
@@ -1788,12 +1788,18 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
1788
1788
  const supportsThinking = !resolvedModel.includes('haiku');
1789
1789
  const needsThinking = !isHeartbeat && (isPlanStep || isUnleashed || !isCron);
1790
1790
  const computedThinking = thinking ?? (supportsThinking && needsThinking ? { type: 'adaptive' } : undefined);
1791
- // Haiku rejects user-configurable task budgets with a 400 ("This model
1792
- // does not support user-configurable task budgets"). Only pass
1793
- // taskBudget to models that accept it otherwise every Haiku cron
1794
- // run dies on arrival and (historically) got mis-classified as a
1795
- // permanent "budget exceeded" failure.
1796
- const supportsTaskBudget = !resolvedModel.includes('haiku');
1791
+ // ── taskBudget: don't pass to the SDK ─────────────────────────
1792
+ // The Anthropic API now rejects `taskBudget` for both Haiku AND Sonnet
1793
+ // ("This model does not support user-configurable task budgets" 400).
1794
+ // We previously gated by !haiku, but that left Sonnet crons (e.g.,
1795
+ // ross-the-sdr:reply-detection) failing on every run. Cost is
1796
+ // informational on a Claude subscription anyway — `maxTurns` and the
1797
+ // wall-clock cap (`maxHours` for unleashed) are the actual brakes.
1798
+ //
1799
+ // computedTaskBudget is still computed below for any future telemetry
1800
+ // path that wants to log "soft target" values, but it is intentionally
1801
+ // never passed into sdkOptions.
1802
+ const supportsTaskBudget = false;
1797
1803
  // 1M context beta: enable for Sonnet when toggled and context-heavy work benefits
1798
1804
  const isSonnet = resolvedModel.includes('sonnet');
1799
1805
  const computedBetas = ENABLE_1M_CONTEXT && isSonnet
@@ -457,11 +457,27 @@ export class CronScheduler {
457
457
  // phase updates for deep-mode runs get routed back to the originating
458
458
  // session instead of fanning out to every registered channel.
459
459
  const isDeepMode = (jobName) => jobName.startsWith('deep-');
460
- // Wire up push notifications for unleashed task completions
460
+ // Wire up push notifications for unleashed task completions.
461
+ //
462
+ // This callback is only meant for AD-HOC unleashed tasks (chat-triggered
463
+ // "deep mode" follow-ups that didn't go through a registered job). Three
464
+ // other paths already own their own delivery and would otherwise produce
465
+ // double-dispatches:
466
+ //
467
+ // 1. deep-mode (`deep-*`) → router handles delivery via _deliverDeepResult
468
+ // 2. background tasks (`bg:*`) → processBackgroundTasks dispatches result
469
+ // 3. registered cron jobs → cron-runner success path dispatches at line ~1115
470
+ //
471
+ // Each path is gated below; without the guards Sasha's morning brief was
472
+ // landing twice in her Discord channel.
461
473
  this.gateway.setUnleashedCompleteCallback((jobName, result) => {
462
474
  this.completedJobs.set(jobName, Date.now());
463
475
  if (isDeepMode(jobName))
464
- return; // router handles delivery via _deliverDeepResult
476
+ return; // (1) deep-mode router
477
+ if (jobName.startsWith('bg:'))
478
+ return; // (2) background-task dispatcher
479
+ if (this.jobs.some(j => j.name === jobName))
480
+ return; // (3) registered cron job
465
481
  if (result && result !== '__NOTHING__') {
466
482
  const slug = jobName.includes(':') ? jobName.split(':')[0] : undefined;
467
483
  // Strip system metadata for clean conversational delivery
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.0.89",
3
+ "version": "1.0.90",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",