clementine-agent 1.18.178 → 1.18.179

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.
@@ -19,7 +19,7 @@ import { TunnelManager } from './tunnel.js';
19
19
  import { AgentManager } from '../agent/agent-manager.js';
20
20
  import { discoverMcpServers, getClaudeIntegrations, KNOWN_MCP_DESCRIPTIONS } from '../agent/mcp-bridge.js';
21
21
  import { buildBuilderEnrichedMessage, builderSessionKey } from '../dashboard/builder/prompt.js';
22
- import { AGENTS_DIR, MEMORY_FILE, SESSIONS_FILE, TIMEZONE, applyOneMillionContextRecovery, currentTimeZone, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../config.js';
22
+ import { AGENTS_DIR, MEMORY_FILE, SESSIONS_FILE, TIMEZONE, applyOneMillionContextRecovery, currentTimeZone, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, setEnvOverride, } from '../config.js';
23
23
  import { parseTasks } from '../tools/shared.js';
24
24
  // 1.18.160 — also pull parseCronJobs + parseAgentCronJobs so getCronJobs()
25
25
  // returns the same merged set the runtime fires (CRON.md + agent CRON +
@@ -8724,6 +8724,11 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
8724
8724
  content = content.trimEnd() + `\n${key}=${value}\n`;
8725
8725
  }
8726
8726
  writeFileSync(ENV_PATH, content, { mode: 0o600 });
8727
+ // Always mirror the disk write into the live env cache. Without this,
8728
+ // BUDGET.* and any other getEnv-backed config stays at the value it
8729
+ // was first read with — that's how "Saved $0 in the dashboard" can
8730
+ // coexist with "Hit the $1.00 cron budget cap" in the same minute.
8731
+ setEnvOverride(key, value);
8727
8732
  }
8728
8733
  function deleteEnvValue(key) {
8729
8734
  if (!existsSync(ENV_PATH))
@@ -8731,6 +8736,9 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
8731
8736
  const re = new RegExp(`^${key}=.*\n?`, 'm');
8732
8737
  const content = readFileSync(ENV_PATH, 'utf-8').replace(re, '');
8733
8738
  writeFileSync(ENV_PATH, content, { mode: 0o600 });
8739
+ // Mirror the delete so live readers don't keep seeing the cached value.
8740
+ setEnvOverride(key, '');
8741
+ delete process.env[key];
8734
8742
  }
8735
8743
  const DASHBOARD_BUDGET_ROWS = [
8736
8744
  { key: 'BUDGET_CHAT_USD', value: '5', label: 'Chat', hint: 'Per interactive chat turn' },
@@ -8786,8 +8794,9 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
8786
8794
  return { ok: false, error: 'Budget cap is too high for the dashboard. Use the CLI if you really need a cap above $1000.' };
8787
8795
  }
8788
8796
  const normalized = n === 0 ? '0' : String(Math.round(n * 100) / 100);
8797
+ // `writeEnvValue` mirrors into the live env cache, so BUDGET.* (now
8798
+ // backed by getters) sees the new value on the very next tool call.
8789
8799
  writeEnvValue(key, normalized);
8790
- process.env[key] = normalized;
8791
8800
  return { ok: true, value: normalized };
8792
8801
  }
8793
8802
  function readRecentDashboardChatFailures(limit = 5) {
@@ -9046,7 +9055,7 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
9046
9055
  }
9047
9056
  res.json({
9048
9057
  ok: true,
9049
- message: `${key} set to ${formatDashboardBudgetValue(result.value)}. Restart Clementine to apply to running workers.`,
9058
+ message: `${key} set to ${formatDashboardBudgetValue(result.value)}. Applied to running workers immediately.`,
9050
9059
  });
9051
9060
  }
9052
9061
  catch (err) {
@@ -9060,11 +9069,11 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
9060
9069
  let message;
9061
9070
  if (preset === 'defaults' || preset === 'standard') {
9062
9071
  writes = DASHBOARD_BUDGET_ROWS.map(row => ({ key: row.key, value: row.value }));
9063
- message = 'Restored the standard spend caps. Restart Clementine to apply to running workers.';
9072
+ message = 'Restored the standard spend caps. Applied to running workers immediately.';
9064
9073
  }
9065
9074
  else if (preset === 'uncapped' || preset === 'off' || preset === 'none') {
9066
9075
  writes = DASHBOARD_BUDGET_ROWS.map(row => ({ key: row.key, value: '0' }));
9067
- message = 'Removed spend caps by setting all budget values to 0. Restart Clementine for the change to take effect on running workers. (1M context mode is separate — use Force 200K or Safe Recovery for 1M errors.)';
9076
+ message = 'Removed spend caps by setting all budget values to 0. Applied to running workers immediately. (1M context mode is separate — use Force 200K or Safe Recovery for 1M errors.)';
9068
9077
  }
9069
9078
  else {
9070
9079
  res.status(400).json({ error: 'preset must be defaults or uncapped' });
package/dist/config.d.ts CHANGED
@@ -40,6 +40,17 @@ export declare function usesOneMillionContext(model: string | null | undefined,
40
40
  export declare function getEnv(key: string, fallback?: string): string;
41
41
  /** Merged view of process.env overlaid with .env. Use for classifyIntegrations / summarizeIntegrationStatus. */
42
42
  export declare function envSnapshot(): Record<string, string | undefined>;
43
+ /**
44
+ * Hot-update a config value at runtime. Call this from any code path that
45
+ * persists a config change (e.g. dashboard `/api/budgets/set`) so the
46
+ * in-module `env` cache stays in sync with what's on disk + in process.env.
47
+ *
48
+ * Without this, `getEnv` keeps returning the value that was read from .env
49
+ * at module init and frozen objects like BUDGET stay stale until the
50
+ * daemon restarts — that's how a "Budgets at zero in the dashboard" UI can
51
+ * coexist with a `Hit the $1.00 cron budget cap` error on the same minute.
52
+ */
53
+ export declare function setEnvOverride(key: string, value: string): void;
43
54
  /** Test-only: clear the keychain ref cache so re-resolution can be tested. */
44
55
  export declare function _resetKeychainRefCache(): void;
45
56
  /**
@@ -83,14 +94,14 @@ export declare const ASSISTANT_EXPERIENCE: {
83
94
  export declare const shellEscape: typeof _shellEscape;
84
95
  export declare const MODELS: Models;
85
96
  export declare const BUDGET: {
86
- heartbeat: number;
87
- cronT1: number;
88
- cronT2: number;
89
- chat: number;
90
- unleashedPhase: undefined;
91
- memoryExtraction: undefined;
92
- summarization: undefined;
93
- reflection: undefined;
97
+ readonly heartbeat: number;
98
+ readonly cronT1: number;
99
+ readonly cronT2: number;
100
+ readonly chat: number;
101
+ readonly unleashedPhase: number | undefined;
102
+ readonly memoryExtraction: number | undefined;
103
+ readonly summarization: number | undefined;
104
+ readonly reflection: number | undefined;
94
105
  };
95
106
  export declare const MEMORY_JANITOR: {
96
107
  consolidatedExpireDays: number;
package/dist/config.js CHANGED
@@ -285,6 +285,20 @@ export function getEnv(key, fallback = '') {
285
285
  export function envSnapshot() {
286
286
  return { ...process.env, ...env };
287
287
  }
288
+ /**
289
+ * Hot-update a config value at runtime. Call this from any code path that
290
+ * persists a config change (e.g. dashboard `/api/budgets/set`) so the
291
+ * in-module `env` cache stays in sync with what's on disk + in process.env.
292
+ *
293
+ * Without this, `getEnv` keeps returning the value that was read from .env
294
+ * at module init and frozen objects like BUDGET stay stale until the
295
+ * daemon restarts — that's how a "Budgets at zero in the dashboard" UI can
296
+ * coexist with a `Hit the $1.00 cron budget cap` error on the same minute.
297
+ */
298
+ export function setEnvOverride(key, value) {
299
+ env[key] = value;
300
+ process.env[key] = value;
301
+ }
288
302
  /** Test-only: clear the keychain ref cache so re-resolution can be tested. */
289
303
  export function _resetKeychainRefCache() {
290
304
  resolvedKeychainRefs.clear();
@@ -379,11 +393,14 @@ export const MODELS = {
379
393
  // User-tunable via `clementine config set BUDGET_<NAME>_USD <value>`
380
394
  // (writes to ~/.clementine/.env, survives npm update -g) or via
381
395
  // `budgets.*` keys in clementine.json.
396
+ // Live getters — each property re-reads .env + process.env on access so a
397
+ // dashboard write (via setEnvOverride) takes effect on the *next* tool call
398
+ // without needing a daemon restart. Defaults match the previous fixed values.
382
399
  export const BUDGET = {
383
- heartbeat: getEnvOrJsonNumber('BUDGET_HEARTBEAT_USD', json.budgets?.heartbeat, 0.25), // per heartbeat (Haiku)
384
- cronT1: getEnvOrJsonNumber('BUDGET_CRON_T1_USD', json.budgets?.cronT1, 0.75), // per tier-1 cron job
385
- cronT2: getEnvOrJsonNumber('BUDGET_CRON_T2_USD', json.budgets?.cronT2, 1.50), // per tier-2 cron job
386
- chat: getEnvOrJsonNumber('BUDGET_CHAT_USD', json.budgets?.chat, 5.00), // per interactive chat
400
+ get heartbeat() { return getEnvOrJsonNumber('BUDGET_HEARTBEAT_USD', json.budgets?.heartbeat, 0.25); },
401
+ get cronT1() { return getEnvOrJsonNumber('BUDGET_CRON_T1_USD', json.budgets?.cronT1, 0.75); },
402
+ get cronT2() { return getEnvOrJsonNumber('BUDGET_CRON_T2_USD', json.budgets?.cronT2, 1.50); },
403
+ get chat() { return getEnvOrJsonNumber('BUDGET_CHAT_USD', json.budgets?.chat, 5.00); },
387
404
  unleashedPhase: undefined,
388
405
  memoryExtraction: undefined,
389
406
  summarization: undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.18.178",
3
+ "version": "1.18.179",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",