@nexus-cortex/core 4.93.0 → 4.95.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.
Files changed (55) hide show
  1. package/{.env.example → .env.defaults} +134 -25
  2. package/dist/config/MentorshipConfigService.d.ts +0 -1
  3. package/dist/config/MentorshipConfigService.d.ts.map +1 -1
  4. package/dist/config/MentorshipConfigService.js +10 -5
  5. package/dist/config/MentorshipConfigService.js.map +1 -1
  6. package/dist/config/SettingsLoader.d.ts +92 -11
  7. package/dist/config/SettingsLoader.d.ts.map +1 -1
  8. package/dist/config/SettingsLoader.js +331 -131
  9. package/dist/config/SettingsLoader.js.map +1 -1
  10. package/dist/config/effectiveConfig.d.ts +1 -1
  11. package/dist/config/effectiveConfig.d.ts.map +1 -1
  12. package/dist/config/effectiveConfig.js +10 -1
  13. package/dist/config/effectiveConfig.js.map +1 -1
  14. package/dist/middleware/HelperModelMiddleware.d.ts +32 -0
  15. package/dist/middleware/HelperModelMiddleware.d.ts.map +1 -1
  16. package/dist/middleware/HelperModelMiddleware.js +57 -5
  17. package/dist/middleware/HelperModelMiddleware.js.map +1 -1
  18. package/dist/models/cards/deepseek/deepseek-v4-flash.d.ts.map +1 -1
  19. package/dist/models/cards/deepseek/deepseek-v4-flash.js +4 -2
  20. package/dist/models/cards/deepseek/deepseek-v4-flash.js.map +1 -1
  21. package/dist/orchestrator/CortexOrchestrator.d.ts +34 -0
  22. package/dist/orchestrator/CortexOrchestrator.d.ts.map +1 -1
  23. package/dist/orchestrator/CortexOrchestrator.js +292 -6
  24. package/dist/orchestrator/CortexOrchestrator.js.map +1 -1
  25. package/dist/orchestrator/OrchestratorFactory.js +1 -1
  26. package/dist/orchestrator/OrchestratorFactory.js.map +1 -1
  27. package/dist/orchestrator/displayHistorySanitizer.d.ts +29 -0
  28. package/dist/orchestrator/displayHistorySanitizer.d.ts.map +1 -0
  29. package/dist/orchestrator/displayHistorySanitizer.js +62 -0
  30. package/dist/orchestrator/displayHistorySanitizer.js.map +1 -0
  31. package/dist/orchestrator/loopToolBlock.d.ts +46 -0
  32. package/dist/orchestrator/loopToolBlock.d.ts.map +1 -0
  33. package/dist/orchestrator/loopToolBlock.js +66 -0
  34. package/dist/orchestrator/loopToolBlock.js.map +1 -0
  35. package/dist/tools/context-management/InitCortexContext.js +1 -1
  36. package/dist/tools/context-management/InitCortexContext.js.map +1 -1
  37. package/dist/training/DecisionStore.d.ts +1 -1
  38. package/dist/training/DecisionStore.d.ts.map +1 -1
  39. package/dist/training/DecisionStore.js.map +1 -1
  40. package/dist/training/deadlineExitMentor.d.ts +67 -0
  41. package/dist/training/deadlineExitMentor.d.ts.map +1 -0
  42. package/dist/training/deadlineExitMentor.js +99 -0
  43. package/dist/training/deadlineExitMentor.js.map +1 -0
  44. package/dist/training/endTurnResolver.d.ts +14 -3
  45. package/dist/training/endTurnResolver.d.ts.map +1 -1
  46. package/dist/training/endTurnResolver.js +27 -10
  47. package/dist/training/endTurnResolver.js.map +1 -1
  48. package/dist/training/loopExitPlanner.d.ts +30 -0
  49. package/dist/training/loopExitPlanner.d.ts.map +1 -0
  50. package/dist/training/loopExitPlanner.js +61 -0
  51. package/dist/training/loopExitPlanner.js.map +1 -0
  52. package/dist/training/toolOutcome.d.ts.map +1 -1
  53. package/dist/training/toolOutcome.js +33 -2
  54. package/dist/training/toolOutcome.js.map +1 -1
  55. package/package.json +5 -5
@@ -8,7 +8,7 @@ import * as fs from 'fs';
8
8
  import * as path from 'path';
9
9
  import * as os from 'os';
10
10
  import { fileURLToPath } from 'url';
11
- import { DEFAULT_SETTINGS, validateSetting, getSettingMetadata } from './SettingsSchema.js';
11
+ import { DEFAULT_SETTINGS, SETTINGS_METADATA, validateSetting, getSettingMetadata } from './SettingsSchema.js';
12
12
  /**
13
13
  * Parse .env file content into key-value pairs
14
14
  */
@@ -49,20 +49,89 @@ export function getGlobalConfigDir() {
49
49
  export function getGlobalEnvPath() {
50
50
  return path.join(getGlobalConfigDir(), '.env');
51
51
  }
52
+ /**
53
+ * Resolve the shipped `.env.defaults` — the read-live default config layer.
54
+ *
55
+ * This file is committed + ships in the package and is READ directly (never copied
56
+ * into the user's .env), so a package upgrade ships new defaults automatically. It is
57
+ * loaded as the LOWEST-precedence layer (below the user's ~/.cortex/.env, below the
58
+ * real shell env) and also overlays DEFAULT_SETTINGS as the effective floor.
59
+ *
60
+ * Search order mirrors the (legacy) seed: the caller's packageRoot, then cwd, then
61
+ * CORE's own root — core is a dependency of every entry point and is build-synced
62
+ * with the root canonical, so the file exists for any current/future bin even when
63
+ * the calling package doesn't ship its own copy.
64
+ */
65
+ export function resolveDefaultsEnvPath(packageRoot) {
66
+ const cwd = process.cwd();
67
+ const coreOwnRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
68
+ return [
69
+ packageRoot ? path.join(packageRoot, '.env.defaults') : undefined,
70
+ path.join(cwd, '.env.defaults'),
71
+ path.join(coreOwnRoot, '.env.defaults'),
72
+ ].find((p) => !!p && fs.existsSync(p));
73
+ }
74
+ /**
75
+ * The keys-only seed for a fresh `~/.cortex/.env`: the secret keys + a short header,
76
+ * NO levers. Harness defaults ship in `.env.defaults` and are read live, so freezing
77
+ * them into the user's file is exactly what blocks upgrades from propagating new
78
+ * defaults. Seeding only the keys keeps the file sparse — an upgrade brings new
79
+ * defaults with nothing to regenerate and no keys to re-enter.
80
+ */
81
+ export function keysOnlyStub() {
82
+ const secretKeys = SETTINGS_METADATA.filter(m => m.secret).map(m => m.key);
83
+ const header = '# Nexus Cortex — your keys & overrides\n' +
84
+ '#\n' +
85
+ '# Put your provider API keys here (or run: cortex config set <KEY> <value>).\n' +
86
+ '# Harness DEFAULTS are NOT in this file — they ship in the package (.env.defaults)\n' +
87
+ '# and are read live, so upgrades bring new defaults with nothing to regenerate and\n' +
88
+ '# no keys to re-enter. Only set a lever here to DIVERGE from the shipped default;\n' +
89
+ '# an uncommented KEY=value here overrides it.\n' +
90
+ '\n';
91
+ return header + secretKeys.map(k => `${k}=`).join('\n') + '\n';
92
+ }
93
+ let _effectiveDefaults;
94
+ /**
95
+ * `DEFAULT_SETTINGS` overlaid with the shipped `.env.defaults` — the EFFECTIVE floor.
96
+ *
97
+ * The read-live default layer reaches most levers through `process.env` (bootstrapEnv
98
+ * loads `.env.defaults` as the lowest base file), but a handful of merge keys don't
99
+ * consult `process.env`. Overlaying `.env.defaults` onto `DEFAULT_SETTINGS` makes the
100
+ * shipped file authoritative for EVERY lever. Secrets are untouched (they are Omitted
101
+ * from `DEFAULT_SETTINGS`, so the loop never visits them). Cached — the shipped file
102
+ * does not change at runtime.
103
+ */
104
+ export function getEffectiveDefaults() {
105
+ if (_effectiveDefaults)
106
+ return _effectiveDefaults;
107
+ const d = { ...DEFAULT_SETTINGS };
108
+ const shipped = resolveDefaultsEnvPath();
109
+ if (shipped) {
110
+ try {
111
+ const parsed = parseEnvFile(fs.readFileSync(shipped, 'utf-8'));
112
+ for (const k of Object.keys(DEFAULT_SETTINGS)) {
113
+ const v = parsed[k];
114
+ if (v !== undefined && v !== '')
115
+ d[k] = v;
116
+ }
117
+ }
118
+ catch { /* keep the hardcoded floor */ }
119
+ }
120
+ _effectiveDefaults = d;
121
+ return d;
122
+ }
52
123
  /**
53
124
  * THE canonical .env → process.env bootstrap for every CLI/TUI entry point.
54
125
  *
55
126
  * One implementation, so a consumer never hand-rolls dotenv loading (and can't
56
- * forget the global `~/.cortex/.env`). This function ONLY READS + layers existing
57
- * .env files into process.env — it never CREATES one. The global `~/.cortex/.env`
58
- * is seeded SEPARATELY, on first run, by the bin: `packages/cli/bin/cortex.js`
59
- * copies the shipped `.env.example` → `~/.cortex/.env` when that file is missing
60
- * (the Docker image does the same copy at build time). All bins call this ONCE at
61
- * startup; a change here takes effect for every launcher.
127
+ * forget the global `~/.cortex/.env`). It seeds a KEYS-ONLY `~/.cortex/.env` on first
128
+ * run (below), then READS + layers the .env files into process.env. All bins call this
129
+ * ONCE at startup; a change here takes effect for every launcher.
62
130
  *
63
- * Precedence (highest → lowest): a `.env.local` beside any of the base files wins
64
- * over everything; then project `./.env` (or `packageRoot/.env`) beats the global
65
- * `~/.cortex/.env`. Matches `loadEnvFile()`'s layering.
131
+ * Precedence (highest → lowest): a `.env.local` beside any of the base files wins over
132
+ * everything; then project `./.env` (or `packageRoot/.env`) beats the global
133
+ * `~/.cortex/.env`, which beats the shipped `.env.defaults` (the read-live default
134
+ * layer, appended last). Matches `loadEnvFile()`'s layering.
66
135
  *
67
136
  * FIRST-WINS over the real environment: a value already present in `process.env`
68
137
  * (e.g. a key the hosting container injected) is NEVER overwritten by a base .env
@@ -75,48 +144,39 @@ export function getGlobalEnvPath() {
75
144
  */
76
145
  export function bootstrapEnv(packageRoot) {
77
146
  const cwd = process.cwd();
78
- // FIRST-ACTIVATION SEED: the first time ANY entry point activates the library
79
- // (server, `cortex`, `cortex-cli`, `fuzzycortex`, `neoncortex`, …), create BOTH
80
- // .env files from the shipped `.env.example` when missing — the global
81
- // `~/.cortex/.env` AND a USER-FINDABLE `packageRoot/.env` (the install dir the user
82
- // can open in a file manager). Byte-identical at creation, so no conflict; the user
83
- // edits the install-dir copy and it presides over the global (precedence below).
84
- // Delete either file and the next activation re-seeds it. Best-effort — a read-only
85
- // location is skipped, and a real injected env still works (blank template = unset).
86
- // 🔴 GUARANTEED TEMPLATE (operator directive: the seed must work "no matter how
87
- // the library or published npm package is invoked"): the calling package's own
88
- // .env.example is preferred, but 4 of 5 entry-point packages historically did
89
- // NOT ship one — the server entry point silently skipped seeding, so bench/task
90
- // containers (server-direct boots) never got a .env at all (found 2026-08-31
91
- // via the TB2.1 SearchTools dead-door). CORE ships the template itself
92
- // (build-synced from the repo root) and is a dependency of every entry point,
93
- // so this fallback makes the seed unconditional for any current or future bin.
94
- const coreOwnRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
95
- const examplePath = [
96
- packageRoot ? path.join(packageRoot, '.env.example') : undefined,
97
- path.join(cwd, '.env.example'),
98
- path.join(coreOwnRoot, '.env.example'),
99
- ].find((p) => !!p && fs.existsSync(p));
100
- if (examplePath) {
101
- const seedTargets = [
102
- getGlobalEnvPath(), // global ~/.cortex/.env
103
- ...(packageRoot ? [path.join(packageRoot, '.env')] : []), // user-findable install-dir .env
104
- ];
105
- for (const target of seedTargets) {
106
- try {
107
- if (!fs.existsSync(target)) {
108
- fs.mkdirSync(path.dirname(target), { recursive: true });
109
- fs.copyFileSync(examplePath, target);
110
- }
111
- }
112
- catch { /* read-only dir — skip; the file just won't exist for this launch */ }
147
+ // FIRST-ACTIVATION SEED — a KEYS-ONLY `~/.cortex/.env` (somewhere to put secrets),
148
+ // NOT a copy of every lever. The first time ANY entry point activates the library
149
+ // (server, `cortex`, `cortex-cli`, `fuzzycortex`, `neoncortex`, …) and no global
150
+ // config exists yet, write the sparse keys stub. Harness DEFAULTS are read live from
151
+ // the shipped `.env.defaults` (loaded below as the lowest layer) — freezing them into
152
+ // the user's file is exactly what USED to block upgrades from propagating new
153
+ // defaults, so we no longer seed levers or a `packageRoot/.env` copy. Best-effort —
154
+ // a read-only home is skipped, and a real injected key still works (blank = unset).
155
+ const defaultsPath = resolveDefaultsEnvPath(packageRoot);
156
+ try {
157
+ const target = getGlobalEnvPath();
158
+ if (!fs.existsSync(target)) {
159
+ fs.mkdirSync(path.dirname(target), { recursive: true });
160
+ fs.writeFileSync(target, keysOnlyStub(), 'utf-8');
113
161
  }
114
162
  }
115
- // Base files, HIGHEST precedence first (first-wins → earlier entries win).
163
+ catch { /* read-only home — skip; the file just won't exist for this launch */ }
164
+ // 🔴 NO auto-migration here. `migrateGlobalConfigToSparse()` used to run at bootstrap and
165
+ // COLLAPSE a full-snapshot ~/.cortex/.env in place — which silently mutated a live/working
166
+ // config (it clobbered the dev config 2026-09-06). Migration is now OPT-IN only (a deliberate
167
+ // `cortex config migrate` command with a diff + confirm); bootstrap must NEVER modify the
168
+ // user's config. Propagation for the common case already works without it: any lever a user
169
+ // did not override flows from the shipped `.env.defaults` (read-live) on upgrade.
170
+ // Base files, HIGHEST precedence first (first-wins → earlier entries win). The
171
+ // shipped `.env.defaults` is appended LAST = lowest precedence: it is the read-live
172
+ // default layer, below the user's files and below the real injected env — so a user
173
+ // override (or a bench env-override) always wins, and unset levers flow from the
174
+ // shipped defaults.
116
175
  const baseFiles = [
117
176
  path.join(cwd, '.env'),
118
177
  ...(packageRoot ? [path.join(packageRoot, '.env')] : []),
119
178
  getGlobalEnvPath(),
179
+ ...(defaultsPath ? [defaultsPath] : []),
120
180
  ];
121
181
  // `.local` overrides beside each base location (these DO override existing env).
122
182
  const localFiles = [
@@ -297,6 +357,124 @@ export function writeEnvSettings(projectPath, settings) {
297
357
  }
298
358
  return results;
299
359
  }
360
+ /**
361
+ * Surgically REMOVE a setting's line from the .env file (delete the `KEY=...` line and
362
+ * its value), preserving every other line and all comments. This is the correct "reset
363
+ * to default" in the read-live model: deleting a user's override line makes the lever
364
+ * fall back to the shipped `.env.defaults` — so the user rejoins the default flow and
365
+ * picks up future improvements. (Writing the current default VALUE would re-freeze it.)
366
+ * A key that isn't present is a no-op success.
367
+ */
368
+ export function removeEnvSetting(projectPath, key) {
369
+ const envPath = path.join(projectPath, '.env');
370
+ if (!fs.existsSync(envPath)) {
371
+ return { success: true, newValue: '' }; // nothing to remove
372
+ }
373
+ let content;
374
+ try {
375
+ content = fs.readFileSync(envPath, 'utf-8');
376
+ }
377
+ catch (error) {
378
+ return { success: false, error: `Failed to read .env file: ${error.message}`, newValue: '' };
379
+ }
380
+ const previousValue = parseEnvFile(content)[key];
381
+ if (previousValue === undefined) {
382
+ return { success: true, newValue: '' }; // key not set — nothing to remove
383
+ }
384
+ // Drop the KEY=... line (allow leading whitespace); leave comments/other lines intact.
385
+ const keyLine = new RegExp(`^[ \\t]*${key}=.*(?:\\r?\\n)?`, 'm');
386
+ const newContent = content.replace(keyLine, '');
387
+ try {
388
+ fs.writeFileSync(envPath, newContent, 'utf-8');
389
+ return { success: true, previousValue, newValue: '' };
390
+ }
391
+ catch (error) {
392
+ return { success: false, error: `Failed to write .env file: ${error.message}`, previousValue, newValue: '' };
393
+ }
394
+ }
395
+ /** Set a single setting in the GLOBAL ~/.cortex/.env (surgical, sparse). The canonical
396
+ * write target for every /config surface — harness config is global (identical from any
397
+ * directory), so a user's choice lives in one place and survives package updates. */
398
+ export function setGlobalSetting(key, value) {
399
+ return writeEnvSetting(getGlobalConfigDir(), key, value);
400
+ }
401
+ /** Remove a single setting's override line from the GLOBAL ~/.cortex/.env (reset-to-default). */
402
+ export function removeGlobalSetting(key) {
403
+ return removeEnvSetting(getGlobalConfigDir(), key);
404
+ }
405
+ /** The SHIPPED default value for a lever, read from the live `.env.defaults` (covers every
406
+ * lever, including the ones not in DEFAULT_SETTINGS). '' if the file/key is absent. Used
407
+ * to restore a lever to its shipped default after removing a user override. */
408
+ export function getShippedDefault(key) {
409
+ const p = resolveDefaultsEnvPath();
410
+ if (!p)
411
+ return '';
412
+ try {
413
+ return parseEnvFile(fs.readFileSync(p, 'utf-8'))[key] ?? '';
414
+ }
415
+ catch {
416
+ return '';
417
+ }
418
+ }
419
+ /**
420
+ * ONE-TIME migration for EXISTING users upgrading to the read-live model. The old install
421
+ * seeded a FULL copy of every lever into `~/.cortex/.env`, which now FREEZES those levers
422
+ * (a user value beats the shipped `.env.defaults`), so an upgrade would never reach them.
423
+ *
424
+ * This collapses that full snapshot back to SPARSE: a lever line is removed when its value
425
+ * equals a DEFAULT — either the new shipped default (`.env.defaults`) OR the hardcoded
426
+ * `DEFAULT_SETTINGS` floor (which still holds the OLD pre-reson defaults). Matching either
427
+ * means the user never deliberately chose it, so removing it lets the lever inherit the
428
+ * current shipped default (and every future one). Genuine divergences and ALL API keys are
429
+ * preserved untouched. Marker-gated (`~/.cortex/.env-migrated`) so it runs exactly once;
430
+ * best-effort (never blocks boot); only acts on a FULL snapshot, never a sparse file.
431
+ */
432
+ export function migrateGlobalConfigToSparse() {
433
+ const dir = getGlobalConfigDir();
434
+ const marker = path.join(dir, '.env-migrated');
435
+ const envPath = getGlobalEnvPath();
436
+ try {
437
+ if (fs.existsSync(marker) || !fs.existsSync(envPath))
438
+ return { migrated: false, removed: 0 };
439
+ const shippedPath = resolveDefaultsEnvPath();
440
+ const shipped = shippedPath ? parseEnvFile(fs.readFileSync(shippedPath, 'utf-8')) : {};
441
+ const userEnv = parseEnvFile(fs.readFileSync(envPath, 'utf-8'));
442
+ const secretKeys = new Set(SETTINGS_METADATA.filter(m => m.secret).map(m => m.key));
443
+ const leverEntries = Object.entries(userEnv).filter(([k]) => !secretKeys.has(k));
444
+ // Only act on a FULL snapshot (the old copy-seed signature) — never disturb a file the
445
+ // user has already curated down to a few sparse overrides.
446
+ if (leverEntries.length <= 15) {
447
+ try {
448
+ fs.writeFileSync(marker, `skipped (sparse) ${new Date().toISOString()}\n`);
449
+ }
450
+ catch { /* ignore */ }
451
+ return { migrated: false, removed: 0 };
452
+ }
453
+ const toRemove = leverEntries
454
+ .filter(([k, v]) => {
455
+ const shippedVal = shipped[k];
456
+ const floorVal = DEFAULT_SETTINGS[k];
457
+ return (shippedVal !== undefined && shippedVal === v) || (floorVal !== undefined && floorVal === v);
458
+ })
459
+ .map(([k]) => k);
460
+ if (toRemove.length > 0) {
461
+ try {
462
+ fs.copyFileSync(envPath, envPath + '.pre-migrate-backup');
463
+ }
464
+ catch { /* best-effort */ }
465
+ for (const k of toRemove)
466
+ removeEnvSetting(dir, k);
467
+ }
468
+ try {
469
+ fs.writeFileSync(marker, `migrated ${new Date().toISOString()} removed=${toRemove.length}\n`);
470
+ }
471
+ catch { /* ignore */ }
472
+ return { migrated: true, removed: toRemove.length };
473
+ }
474
+ catch {
475
+ return { migrated: false, removed: 0 };
476
+ }
477
+ }
300
478
  /**
301
479
  * Get Google/Gemini API key with proper fallback
302
480
  * Priority: GEMINI_API_KEY (recommended by Google) -> GOOGLE_API_KEY (legacy)
@@ -315,6 +493,9 @@ export function mergeWithDefaults(env) {
315
493
  if (!process.env.GEMINI_API_KEY && process.env.GOOGLE_API_KEY) {
316
494
  process.env.GEMINI_API_KEY = process.env.GOOGLE_API_KEY;
317
495
  }
496
+ // Effective floor = hardcoded DEFAULT_SETTINGS overlaid with the shipped .env.defaults
497
+ // (read-live). Used below wherever a value falls through to the code default.
498
+ const D = getEffectiveDefaults();
318
499
  return {
319
500
  // API Keys (no defaults, use empty string if not set)
320
501
  ANTHROPIC_API_KEY: env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY || '',
@@ -332,98 +513,98 @@ export function mergeWithDefaults(env) {
332
513
  CLOUDFLARE_ACCOUNT_ID: env.CLOUDFLARE_ACCOUNT_ID || process.env.CLOUDFLARE_ACCOUNT_ID || '',
333
514
  NVIDIA_API_KEY: env.NVIDIA_API_KEY || process.env.NVIDIA_API_KEY || '',
334
515
  // Anthropic Authentication
335
- ANTHROPIC_AUTH_METHOD: env.ANTHROPIC_AUTH_METHOD || process.env.ANTHROPIC_AUTH_METHOD || DEFAULT_SETTINGS.ANTHROPIC_AUTH_METHOD,
516
+ ANTHROPIC_AUTH_METHOD: env.ANTHROPIC_AUTH_METHOD || process.env.ANTHROPIC_AUTH_METHOD || D.ANTHROPIC_AUTH_METHOD,
336
517
  CLAUDE_CODE_OAUTH_TOKEN: env.CLAUDE_CODE_OAUTH_TOKEN || process.env.CLAUDE_CODE_OAUTH_TOKEN || '',
337
518
  // Prompt Caching
338
- ANTHROPIC_PROMPT_CACHING: env.ANTHROPIC_PROMPT_CACHING || process.env.ANTHROPIC_PROMPT_CACHING || DEFAULT_SETTINGS.ANTHROPIC_PROMPT_CACHING,
519
+ ANTHROPIC_PROMPT_CACHING: env.ANTHROPIC_PROMPT_CACHING || process.env.ANTHROPIC_PROMPT_CACHING || D.ANTHROPIC_PROMPT_CACHING,
339
520
  // Model Configuration
340
- DEFAULT_MODEL_ID: env.DEFAULT_MODEL_ID || DEFAULT_SETTINGS.DEFAULT_MODEL_ID,
341
- HELPER_MODEL_ID: env.HELPER_MODEL_ID || DEFAULT_SETTINGS.HELPER_MODEL_ID,
521
+ DEFAULT_MODEL_ID: env.DEFAULT_MODEL_ID || D.DEFAULT_MODEL_ID,
522
+ HELPER_MODEL_ID: env.HELPER_MODEL_ID || D.HELPER_MODEL_ID,
342
523
  // System Settings
343
- DEBUG: env.DEBUG || DEFAULT_SETTINGS.DEBUG,
344
- USE_EMOJI: env.USE_EMOJI || DEFAULT_SETTINGS.USE_EMOJI,
345
- PROJECT_PATH: env.PROJECT_PATH || DEFAULT_SETTINGS.PROJECT_PATH,
524
+ DEBUG: env.DEBUG || D.DEBUG,
525
+ USE_EMOJI: env.USE_EMOJI || D.USE_EMOJI,
526
+ PROJECT_PATH: env.PROJECT_PATH || D.PROJECT_PATH,
346
527
  // Reactive Mentorship
347
- MENTORSHIP_ENABLED: env.MENTORSHIP_ENABLED || DEFAULT_SETTINGS.MENTORSHIP_ENABLED,
348
- MENTORSHIP_TRIGGER_ON_ERROR: env.MENTORSHIP_TRIGGER_ON_ERROR || DEFAULT_SETTINGS.MENTORSHIP_TRIGGER_ON_ERROR,
349
- MENTORSHIP_ERROR_THRESHOLD: env.MENTORSHIP_ERROR_THRESHOLD || DEFAULT_SETTINGS.MENTORSHIP_ERROR_THRESHOLD,
350
- MENTORSHIP_KEYWORDS_ENABLED: env.MENTORSHIP_KEYWORDS_ENABLED || DEFAULT_SETTINGS.MENTORSHIP_KEYWORDS_ENABLED,
351
- MENTORSHIP_CUSTOM_KEYWORDS: env.MENTORSHIP_CUSTOM_KEYWORDS || DEFAULT_SETTINGS.MENTORSHIP_CUSTOM_KEYWORDS,
352
- MENTORSHIP_HELPER_MODEL: env.MENTORSHIP_HELPER_MODEL || DEFAULT_SETTINGS.MENTORSHIP_HELPER_MODEL,
353
- MENTORSHIP_TURN_BASED_ENABLED: env.MENTORSHIP_TURN_BASED_ENABLED || DEFAULT_SETTINGS.MENTORSHIP_TURN_BASED_ENABLED,
354
- MENTORSHIP_TURN_INTERVAL: env.MENTORSHIP_TURN_INTERVAL || DEFAULT_SETTINGS.MENTORSHIP_TURN_INTERVAL,
355
- MENTORSHIP_INTERLEAVED_THINKING: env.MENTORSHIP_INTERLEAVED_THINKING || DEFAULT_SETTINGS.MENTORSHIP_INTERLEAVED_THINKING,
356
- MENTORSHIP_PATTERN_DETECTION: env.MENTORSHIP_PATTERN_DETECTION || DEFAULT_SETTINGS.MENTORSHIP_PATTERN_DETECTION,
357
- MENTORSHIP_PATTERN_THRESHOLD: env.MENTORSHIP_PATTERN_THRESHOLD || DEFAULT_SETTINGS.MENTORSHIP_PATTERN_THRESHOLD,
358
- MENTORSHIP_ACTIVE_DISCOVERY: env.MENTORSHIP_ACTIVE_DISCOVERY || DEFAULT_SETTINGS.MENTORSHIP_ACTIVE_DISCOVERY,
528
+ MENTORSHIP_ENABLED: env.MENTORSHIP_ENABLED || D.MENTORSHIP_ENABLED,
529
+ MENTORSHIP_TRIGGER_ON_ERROR: env.MENTORSHIP_TRIGGER_ON_ERROR || D.MENTORSHIP_TRIGGER_ON_ERROR,
530
+ MENTORSHIP_ERROR_THRESHOLD: env.MENTORSHIP_ERROR_THRESHOLD || D.MENTORSHIP_ERROR_THRESHOLD,
531
+ MENTORSHIP_KEYWORDS_ENABLED: env.MENTORSHIP_KEYWORDS_ENABLED || D.MENTORSHIP_KEYWORDS_ENABLED,
532
+ MENTORSHIP_CUSTOM_KEYWORDS: env.MENTORSHIP_CUSTOM_KEYWORDS || D.MENTORSHIP_CUSTOM_KEYWORDS,
533
+ MENTORSHIP_HELPER_MODEL: env.MENTORSHIP_HELPER_MODEL || D.MENTORSHIP_HELPER_MODEL,
534
+ MENTORSHIP_TURN_BASED_ENABLED: env.MENTORSHIP_TURN_BASED_ENABLED || D.MENTORSHIP_TURN_BASED_ENABLED,
535
+ MENTORSHIP_TURN_INTERVAL: env.MENTORSHIP_TURN_INTERVAL || D.MENTORSHIP_TURN_INTERVAL,
536
+ MENTORSHIP_INTERLEAVED_THINKING: env.MENTORSHIP_INTERLEAVED_THINKING || D.MENTORSHIP_INTERLEAVED_THINKING,
537
+ MENTORSHIP_PATTERN_DETECTION: env.MENTORSHIP_PATTERN_DETECTION || D.MENTORSHIP_PATTERN_DETECTION,
538
+ MENTORSHIP_PATTERN_THRESHOLD: env.MENTORSHIP_PATTERN_THRESHOLD || D.MENTORSHIP_PATTERN_THRESHOLD,
539
+ MENTORSHIP_ACTIVE_DISCOVERY: env.MENTORSHIP_ACTIVE_DISCOVERY || D.MENTORSHIP_ACTIVE_DISCOVERY,
359
540
  // Turn Summary & Prediction
360
- TURN_SUMMARY_PREDICTION: env.TURN_SUMMARY_PREDICTION || DEFAULT_SETTINGS.TURN_SUMMARY_PREDICTION,
541
+ TURN_SUMMARY_PREDICTION: env.TURN_SUMMARY_PREDICTION || D.TURN_SUMMARY_PREDICTION,
361
542
  // Canon (reactive session capture)
362
- CANON_AUTO_SYNC: env.CANON_AUTO_SYNC || process.env.CANON_AUTO_SYNC || DEFAULT_SETTINGS.CANON_AUTO_SYNC,
363
- CANON_AUTO_SYNC_DEBOUNCE_MS: env.CANON_AUTO_SYNC_DEBOUNCE_MS || process.env.CANON_AUTO_SYNC_DEBOUNCE_MS || DEFAULT_SETTINGS.CANON_AUTO_SYNC_DEBOUNCE_MS,
364
- CANON_STORE: env.CANON_STORE || process.env.CANON_STORE || DEFAULT_SETTINGS.CANON_STORE,
365
- CANON_REPO: env.CANON_REPO || process.env.CANON_REPO || DEFAULT_SETTINGS.CANON_REPO,
543
+ CANON_AUTO_SYNC: env.CANON_AUTO_SYNC || process.env.CANON_AUTO_SYNC || D.CANON_AUTO_SYNC,
544
+ CANON_AUTO_SYNC_DEBOUNCE_MS: env.CANON_AUTO_SYNC_DEBOUNCE_MS || process.env.CANON_AUTO_SYNC_DEBOUNCE_MS || D.CANON_AUTO_SYNC_DEBOUNCE_MS,
545
+ CANON_STORE: env.CANON_STORE || process.env.CANON_STORE || D.CANON_STORE,
546
+ CANON_REPO: env.CANON_REPO || process.env.CANON_REPO || D.CANON_REPO,
366
547
  // Context Management
367
- THINKING_AS_TEXT_FALLBACK: env.THINKING_AS_TEXT_FALLBACK || process.env.THINKING_AS_TEXT_FALLBACK || DEFAULT_SETTINGS.THINKING_AS_TEXT_FALLBACK,
548
+ THINKING_AS_TEXT_FALLBACK: env.THINKING_AS_TEXT_FALLBACK || process.env.THINKING_AS_TEXT_FALLBACK || D.THINKING_AS_TEXT_FALLBACK,
368
549
  // Session Configuration
369
- SESSION_STORAGE_DIR: env.SESSION_STORAGE_DIR || DEFAULT_SETTINGS.SESSION_STORAGE_DIR,
370
- MCP_AUTO_INJECT: env.MCP_AUTO_INJECT || DEFAULT_SETTINGS.MCP_AUTO_INJECT,
371
- AUTORESEARCH_AGENTS: env.AUTORESEARCH_AGENTS || DEFAULT_SETTINGS.AUTORESEARCH_AGENTS,
372
- SYSTEM_MESSAGE_DOC_MAX_BYTES: env.SYSTEM_MESSAGE_DOC_MAX_BYTES || DEFAULT_SETTINGS.SYSTEM_MESSAGE_DOC_MAX_BYTES,
373
- MEMORY_ARCHIVE_MAX_BYTES: env.MEMORY_ARCHIVE_MAX_BYTES || DEFAULT_SETTINGS.MEMORY_ARCHIVE_MAX_BYTES,
550
+ SESSION_STORAGE_DIR: env.SESSION_STORAGE_DIR || D.SESSION_STORAGE_DIR,
551
+ MCP_AUTO_INJECT: env.MCP_AUTO_INJECT || D.MCP_AUTO_INJECT,
552
+ AUTORESEARCH_AGENTS: env.AUTORESEARCH_AGENTS || D.AUTORESEARCH_AGENTS,
553
+ SYSTEM_MESSAGE_DOC_MAX_BYTES: env.SYSTEM_MESSAGE_DOC_MAX_BYTES || D.SYSTEM_MESSAGE_DOC_MAX_BYTES,
554
+ MEMORY_ARCHIVE_MAX_BYTES: env.MEMORY_ARCHIVE_MAX_BYTES || D.MEMORY_ARCHIVE_MAX_BYTES,
374
555
  // Loop Control
375
- MAX_TOOL_ITERATIONS: env.MAX_TOOL_ITERATIONS || DEFAULT_SETTINGS.MAX_TOOL_ITERATIONS,
376
- MAX_CONSECUTIVE_ERRORS: env.MAX_CONSECUTIVE_ERRORS || DEFAULT_SETTINGS.MAX_CONSECUTIVE_ERRORS,
377
- TOOL_BUDGET_SOFT: env.TOOL_BUDGET_SOFT || DEFAULT_SETTINGS.TOOL_BUDGET_SOFT,
378
- TOOL_TIMEOUT_MS: env.TOOL_TIMEOUT_MS || DEFAULT_SETTINGS.TOOL_TIMEOUT_MS,
379
- MAX_LOOP_REPETITIONS: env.MAX_LOOP_REPETITIONS || DEFAULT_SETTINGS.MAX_LOOP_REPETITIONS,
556
+ MAX_TOOL_ITERATIONS: env.MAX_TOOL_ITERATIONS || D.MAX_TOOL_ITERATIONS,
557
+ MAX_CONSECUTIVE_ERRORS: env.MAX_CONSECUTIVE_ERRORS || D.MAX_CONSECUTIVE_ERRORS,
558
+ TOOL_BUDGET_SOFT: env.TOOL_BUDGET_SOFT || D.TOOL_BUDGET_SOFT,
559
+ TOOL_TIMEOUT_MS: env.TOOL_TIMEOUT_MS || D.TOOL_TIMEOUT_MS,
560
+ MAX_LOOP_REPETITIONS: env.MAX_LOOP_REPETITIONS || D.MAX_LOOP_REPETITIONS,
380
561
  // Web Tools
381
- WEB_TOOLS_MODEL: env.WEB_TOOLS_MODEL || process.env.WEB_TOOLS_MODEL || DEFAULT_SETTINGS.WEB_TOOLS_MODEL,
562
+ WEB_TOOLS_MODEL: env.WEB_TOOLS_MODEL || process.env.WEB_TOOLS_MODEL || D.WEB_TOOLS_MODEL,
382
563
  // Server-Side Tools
383
- ENABLE_SERVER_SIDE_TOOLS: env.ENABLE_SERVER_SIDE_TOOLS || process.env.ENABLE_SERVER_SIDE_TOOLS || DEFAULT_SETTINGS.ENABLE_SERVER_SIDE_TOOLS,
384
- XAI_API_MODE: env.XAI_API_MODE || process.env.XAI_API_MODE || DEFAULT_SETTINGS.XAI_API_MODE,
385
- OPENAI_API_MODE: env.OPENAI_API_MODE || process.env.OPENAI_API_MODE || DEFAULT_SETTINGS.OPENAI_API_MODE,
564
+ ENABLE_SERVER_SIDE_TOOLS: env.ENABLE_SERVER_SIDE_TOOLS || process.env.ENABLE_SERVER_SIDE_TOOLS || D.ENABLE_SERVER_SIDE_TOOLS,
565
+ XAI_API_MODE: env.XAI_API_MODE || process.env.XAI_API_MODE || D.XAI_API_MODE,
566
+ OPENAI_API_MODE: env.OPENAI_API_MODE || process.env.OPENAI_API_MODE || D.OPENAI_API_MODE,
386
567
  // Agent Team Monitoring
387
- AGENT_TMUX_MONITOR: env.AGENT_TMUX_MONITOR || process.env.AGENT_TMUX_MONITOR || DEFAULT_SETTINGS.AGENT_TMUX_MONITOR,
568
+ AGENT_TMUX_MONITOR: env.AGENT_TMUX_MONITOR || process.env.AGENT_TMUX_MONITOR || D.AGENT_TMUX_MONITOR,
388
569
  // PTC / Code Execution / Deferred Loading
389
- ENABLE_PTC: env.ENABLE_PTC || process.env.ENABLE_PTC || DEFAULT_SETTINGS.ENABLE_PTC,
390
- ENABLE_WEBTOOLS: env.ENABLE_WEBTOOLS || process.env.ENABLE_WEBTOOLS || DEFAULT_SETTINGS.ENABLE_WEBTOOLS,
391
- VISION_HELPER_MODEL: env.VISION_HELPER_MODEL || process.env.VISION_HELPER_MODEL || DEFAULT_SETTINGS.VISION_HELPER_MODEL,
392
- TOOL_TIMEOUT_MODE: env.TOOL_TIMEOUT_MODE || process.env.TOOL_TIMEOUT_MODE || DEFAULT_SETTINGS.TOOL_TIMEOUT_MODE,
393
- VISION_HANDOFF_MAX: env.VISION_HANDOFF_MAX || process.env.VISION_HANDOFF_MAX || DEFAULT_SETTINGS.VISION_HANDOFF_MAX,
394
- CORTEX_SLICE_NUDGE: env.CORTEX_SLICE_NUDGE || process.env.CORTEX_SLICE_NUDGE || DEFAULT_SETTINGS.CORTEX_SLICE_NUDGE,
395
- ENABLE_LOCAL_CODE_EXECUTION: env.ENABLE_LOCAL_CODE_EXECUTION || process.env.ENABLE_LOCAL_CODE_EXECUTION || DEFAULT_SETTINGS.ENABLE_LOCAL_CODE_EXECUTION,
396
- ENABLE_DEFERRED_TOOL_LOADING: env.ENABLE_DEFERRED_TOOL_LOADING || process.env.ENABLE_DEFERRED_TOOL_LOADING || DEFAULT_SETTINGS.ENABLE_DEFERRED_TOOL_LOADING,
570
+ ENABLE_PTC: env.ENABLE_PTC || process.env.ENABLE_PTC || D.ENABLE_PTC,
571
+ ENABLE_WEBTOOLS: env.ENABLE_WEBTOOLS || process.env.ENABLE_WEBTOOLS || D.ENABLE_WEBTOOLS,
572
+ VISION_HELPER_MODEL: env.VISION_HELPER_MODEL || process.env.VISION_HELPER_MODEL || D.VISION_HELPER_MODEL,
573
+ TOOL_TIMEOUT_MODE: env.TOOL_TIMEOUT_MODE || process.env.TOOL_TIMEOUT_MODE || D.TOOL_TIMEOUT_MODE,
574
+ VISION_HANDOFF_MAX: env.VISION_HANDOFF_MAX || process.env.VISION_HANDOFF_MAX || D.VISION_HANDOFF_MAX,
575
+ CORTEX_SLICE_NUDGE: env.CORTEX_SLICE_NUDGE || process.env.CORTEX_SLICE_NUDGE || D.CORTEX_SLICE_NUDGE,
576
+ ENABLE_LOCAL_CODE_EXECUTION: env.ENABLE_LOCAL_CODE_EXECUTION || process.env.ENABLE_LOCAL_CODE_EXECUTION || D.ENABLE_LOCAL_CODE_EXECUTION,
577
+ ENABLE_DEFERRED_TOOL_LOADING: env.ENABLE_DEFERRED_TOOL_LOADING || process.env.ENABLE_DEFERRED_TOOL_LOADING || D.ENABLE_DEFERRED_TOOL_LOADING,
397
578
  // Model Router
398
- MODEL_ROUTER_ENABLED: env.MODEL_ROUTER_ENABLED || process.env.MODEL_ROUTER_ENABLED || DEFAULT_SETTINGS.MODEL_ROUTER_ENABLED,
399
- MODEL_ROUTER_STRATEGY: env.MODEL_ROUTER_STRATEGY || process.env.MODEL_ROUTER_STRATEGY || DEFAULT_SETTINGS.MODEL_ROUTER_STRATEGY,
400
- MODEL_ROUTER_RECORD: env.MODEL_ROUTER_RECORD || process.env.MODEL_ROUTER_RECORD || DEFAULT_SETTINGS.MODEL_ROUTER_RECORD,
401
- ROUTER_MIN_CONFIDENCE: env.ROUTER_MIN_CONFIDENCE || process.env.ROUTER_MIN_CONFIDENCE || DEFAULT_SETTINGS.ROUTER_MIN_CONFIDENCE,
402
- ROUTER_MIN_SAMPLES: env.ROUTER_MIN_SAMPLES || process.env.ROUTER_MIN_SAMPLES || DEFAULT_SETTINGS.ROUTER_MIN_SAMPLES,
403
- MODEL_ROUTER_EXPLORATION: env.MODEL_ROUTER_EXPLORATION || process.env.MODEL_ROUTER_EXPLORATION || DEFAULT_SETTINGS.MODEL_ROUTER_EXPLORATION,
404
- MODEL_ROUTER_EXCLUDE: env.MODEL_ROUTER_EXCLUDE ?? process.env.MODEL_ROUTER_EXCLUDE ?? DEFAULT_SETTINGS.MODEL_ROUTER_EXCLUDE,
579
+ MODEL_ROUTER_ENABLED: env.MODEL_ROUTER_ENABLED || process.env.MODEL_ROUTER_ENABLED || D.MODEL_ROUTER_ENABLED,
580
+ MODEL_ROUTER_STRATEGY: env.MODEL_ROUTER_STRATEGY || process.env.MODEL_ROUTER_STRATEGY || D.MODEL_ROUTER_STRATEGY,
581
+ MODEL_ROUTER_RECORD: env.MODEL_ROUTER_RECORD || process.env.MODEL_ROUTER_RECORD || D.MODEL_ROUTER_RECORD,
582
+ ROUTER_MIN_CONFIDENCE: env.ROUTER_MIN_CONFIDENCE || process.env.ROUTER_MIN_CONFIDENCE || D.ROUTER_MIN_CONFIDENCE,
583
+ ROUTER_MIN_SAMPLES: env.ROUTER_MIN_SAMPLES || process.env.ROUTER_MIN_SAMPLES || D.ROUTER_MIN_SAMPLES,
584
+ MODEL_ROUTER_EXPLORATION: env.MODEL_ROUTER_EXPLORATION || process.env.MODEL_ROUTER_EXPLORATION || D.MODEL_ROUTER_EXPLORATION,
585
+ MODEL_ROUTER_EXCLUDE: env.MODEL_ROUTER_EXCLUDE ?? process.env.MODEL_ROUTER_EXCLUDE ?? D.MODEL_ROUTER_EXCLUDE,
405
586
  // Endturn Gate / Training
406
- CORTEX_ENDTURN_GATE: env.CORTEX_ENDTURN_GATE || process.env.CORTEX_ENDTURN_GATE || DEFAULT_SETTINGS.CORTEX_ENDTURN_GATE,
407
- CORTEX_TOOL_PROFILE: env.CORTEX_TOOL_PROFILE || process.env.CORTEX_TOOL_PROFILE || DEFAULT_SETTINGS.CORTEX_TOOL_PROFILE,
587
+ CORTEX_ENDTURN_GATE: env.CORTEX_ENDTURN_GATE || process.env.CORTEX_ENDTURN_GATE || D.CORTEX_ENDTURN_GATE,
588
+ CORTEX_TOOL_PROFILE: env.CORTEX_TOOL_PROFILE || process.env.CORTEX_TOOL_PROFILE || D.CORTEX_TOOL_PROFILE,
408
589
  // Decision Store
409
- CORTEX_RECORD_DECISIONS: env.CORTEX_RECORD_DECISIONS || process.env.CORTEX_RECORD_DECISIONS || DEFAULT_SETTINGS.CORTEX_RECORD_DECISIONS,
410
- CORTEX_LOOKUP_PRIOR_DECISIONS: env.CORTEX_LOOKUP_PRIOR_DECISIONS || process.env.CORTEX_LOOKUP_PRIOR_DECISIONS || DEFAULT_SETTINGS.CORTEX_LOOKUP_PRIOR_DECISIONS,
411
- CORTEX_DECISIONS_MAX_BYTES: env.CORTEX_DECISIONS_MAX_BYTES || process.env.CORTEX_DECISIONS_MAX_BYTES || DEFAULT_SETTINGS.CORTEX_DECISIONS_MAX_BYTES,
590
+ CORTEX_RECORD_DECISIONS: env.CORTEX_RECORD_DECISIONS || process.env.CORTEX_RECORD_DECISIONS || D.CORTEX_RECORD_DECISIONS,
591
+ CORTEX_LOOKUP_PRIOR_DECISIONS: env.CORTEX_LOOKUP_PRIOR_DECISIONS || process.env.CORTEX_LOOKUP_PRIOR_DECISIONS || D.CORTEX_LOOKUP_PRIOR_DECISIONS,
592
+ CORTEX_DECISIONS_MAX_BYTES: env.CORTEX_DECISIONS_MAX_BYTES || process.env.CORTEX_DECISIONS_MAX_BYTES || D.CORTEX_DECISIONS_MAX_BYTES,
412
593
  // Runtime
413
- CORTEX_MODE: env.CORTEX_MODE || DEFAULT_SETTINGS.CORTEX_MODE,
414
- CORTEX_UPDATE_POLICY: env.CORTEX_UPDATE_POLICY || process.env.CORTEX_UPDATE_POLICY || DEFAULT_SETTINGS.CORTEX_UPDATE_POLICY,
415
- YOLO: env.YOLO || process.env.YOLO || DEFAULT_SETTINGS.YOLO,
416
- AUTO_RESUME: env.AUTO_RESUME || process.env.AUTO_RESUME || DEFAULT_SETTINGS.AUTO_RESUME,
417
- PORT: env.PORT || process.env.PORT || DEFAULT_SETTINGS.PORT,
418
- CORTEX_SERVER_URL: env.CORTEX_SERVER_URL || DEFAULT_SETTINGS.CORTEX_SERVER_URL,
419
- DEBUG_PAYLOAD: env.DEBUG_PAYLOAD || process.env.DEBUG_PAYLOAD || DEFAULT_SETTINGS.DEBUG_PAYLOAD,
420
- DEBUG_THINKING: env.DEBUG_THINKING || process.env.DEBUG_THINKING || DEFAULT_SETTINGS.DEBUG_THINKING,
421
- ENABLE_SMOKE_TESTS: env.ENABLE_SMOKE_TESTS || process.env.ENABLE_SMOKE_TESTS || DEFAULT_SETTINGS.ENABLE_SMOKE_TESTS,
594
+ CORTEX_MODE: env.CORTEX_MODE || D.CORTEX_MODE,
595
+ CORTEX_UPDATE_POLICY: env.CORTEX_UPDATE_POLICY || process.env.CORTEX_UPDATE_POLICY || D.CORTEX_UPDATE_POLICY,
596
+ YOLO: env.YOLO || process.env.YOLO || D.YOLO,
597
+ AUTO_RESUME: env.AUTO_RESUME || process.env.AUTO_RESUME || D.AUTO_RESUME,
598
+ PORT: env.PORT || process.env.PORT || D.PORT,
599
+ CORTEX_SERVER_URL: env.CORTEX_SERVER_URL || D.CORTEX_SERVER_URL,
600
+ DEBUG_PAYLOAD: env.DEBUG_PAYLOAD || process.env.DEBUG_PAYLOAD || D.DEBUG_PAYLOAD,
601
+ DEBUG_THINKING: env.DEBUG_THINKING || process.env.DEBUG_THINKING || D.DEBUG_THINKING,
602
+ ENABLE_SMOKE_TESTS: env.ENABLE_SMOKE_TESTS || process.env.ENABLE_SMOKE_TESTS || D.ENABLE_SMOKE_TESTS,
422
603
  // Git / PR access control
423
- GIT_ALLOWED_REPOS: env.GIT_ALLOWED_REPOS || process.env.GIT_ALLOWED_REPOS || DEFAULT_SETTINGS.GIT_ALLOWED_REPOS,
424
- GIT_ALLOWED_ACTIONS: env.GIT_ALLOWED_ACTIONS || process.env.GIT_ALLOWED_ACTIONS || DEFAULT_SETTINGS.GIT_ALLOWED_ACTIONS,
604
+ GIT_ALLOWED_REPOS: env.GIT_ALLOWED_REPOS || process.env.GIT_ALLOWED_REPOS || D.GIT_ALLOWED_REPOS,
605
+ GIT_ALLOWED_ACTIONS: env.GIT_ALLOWED_ACTIONS || process.env.GIT_ALLOWED_ACTIONS || D.GIT_ALLOWED_ACTIONS,
425
606
  GIT_AUTH_TOKEN: env.GIT_AUTH_TOKEN || process.env.GIT_AUTH_TOKEN || '',
426
- GIT_HOST: env.GIT_HOST || process.env.GIT_HOST || DEFAULT_SETTINGS.GIT_HOST,
607
+ GIT_HOST: env.GIT_HOST || process.env.GIT_HOST || D.GIT_HOST,
427
608
  GITHUB_WEBHOOK_SECRET: env.GITHUB_WEBHOOK_SECRET || process.env.GITHUB_WEBHOOK_SECRET || '',
428
609
  };
429
610
  }
@@ -581,27 +762,46 @@ export class SettingsLoader {
581
762
  return results;
582
763
  }
583
764
  /**
584
- * Reset a setting to its default value
765
+ * Remove a setting's override line (reset-to-default). Deletes the user's override so
766
+ * the lever falls back to the shipped `.env.defaults` — the correct reset in the
767
+ * read-live model (writing the current default value would re-freeze it).
585
768
  */
769
+ remove(key) {
770
+ const result = removeEnvSetting(this.projectPath, key);
771
+ if (result.success)
772
+ this.reload();
773
+ return result;
774
+ }
775
+ /** Alias — reset a single setting to the shipped default by removing the override. */
586
776
  resetToDefault(key) {
587
- const defaultValue = DEFAULT_SETTINGS[key];
588
- if (defaultValue === undefined) {
589
- return {
590
- success: false,
591
- error: `No default value for ${key}`,
592
- newValue: ''
593
- };
594
- }
595
- return this.set(key, defaultValue);
777
+ return this.remove(key);
778
+ }
779
+ /**
780
+ * True if this key is set as an override in the user's OWN .env file (vs flowing from
781
+ * the shipped .env.defaults). Powers the "your override / shipped default" display.
782
+ */
783
+ isUserOverride(key) {
784
+ return readEnvFileAt(path.join(this.projectPath, '.env'))[key] !== undefined;
785
+ }
786
+ /** All keys the user has overridden in their own .env file (one read — for a display
787
+ * that needs to mark many rows without re-reading the file per row). */
788
+ getOverriddenKeys() {
789
+ return Object.keys(readEnvFileAt(path.join(this.projectPath, '.env')));
596
790
  }
597
791
  /**
598
- * Reset all settings to defaults
792
+ * Reset all LEVERS to defaults by removing their override lines (API keys preserved),
793
+ * so every lever falls back to the shipped `.env.defaults`.
599
794
  */
600
795
  resetAllToDefaults() {
601
796
  const results = {};
602
- for (const [key, value] of Object.entries(DEFAULT_SETTINGS)) {
603
- results[key] = this.set(key, value);
797
+ const secretKeys = new Set(SETTINGS_METADATA.filter(m => m.secret).map(m => m.key));
798
+ const fileEnv = readEnvFileAt(path.join(this.projectPath, '.env'));
799
+ for (const key of Object.keys(fileEnv)) {
800
+ if (secretKeys.has(key))
801
+ continue; // preserve API keys
802
+ results[key] = removeEnvSetting(this.projectPath, key);
604
803
  }
804
+ this.reload();
605
805
  return results;
606
806
  }
607
807
  /**