metame-cli 1.5.16 → 1.5.17
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 +1 -1
- package/scripts/daemon-admin-commands.js +19 -10
- package/scripts/daemon-claude-engine.js +12 -11
- package/scripts/daemon-command-router.js +3 -1
- package/scripts/daemon-engine-runtime.js +43 -5
- package/scripts/daemon-session-store.js +19 -21
- package/scripts/daemon-task-scheduler.js +2 -1
- package/scripts/daemon.js +4 -30
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@ const {
|
|
|
6
6
|
USAGE_CATEGORY_LABEL,
|
|
7
7
|
} = require('./usage-classifier');
|
|
8
8
|
const { IS_WIN } = require('./platform');
|
|
9
|
-
const { ENGINE_MODEL_CONFIG, resolveEngineModel } = require('./daemon-engine-runtime');
|
|
9
|
+
const { ENGINE_MODEL_CONFIG, resolveEngineModel, normalizeClaudeModel } = require('./daemon-engine-runtime');
|
|
10
10
|
const { resolveProjectKey: _resolveProjectKey } = require('./daemon-team-dispatch');
|
|
11
11
|
const {
|
|
12
12
|
parseRemoteTargetRef,
|
|
@@ -1439,7 +1439,7 @@ function createAdminCommandHandler(deps) {
|
|
|
1439
1439
|
return { handled: true, config };
|
|
1440
1440
|
}
|
|
1441
1441
|
|
|
1442
|
-
// /doctor — diagnostics; /fix — restore backup; /reset — reset
|
|
1442
|
+
// /doctor — diagnostics; /fix — restore backup; /reset — reset Claude slot to opus
|
|
1443
1443
|
if (text === '/fix') {
|
|
1444
1444
|
if (restoreConfig()) {
|
|
1445
1445
|
await bot.sendMessage(chatId, '✅ 已从备份恢复配置');
|
|
@@ -1453,7 +1453,8 @@ function createAdminCommandHandler(deps) {
|
|
|
1453
1453
|
backupConfig();
|
|
1454
1454
|
const cfg = yaml.load(fs.readFileSync(CONFIG_FILE, 'utf8')) || {};
|
|
1455
1455
|
if (!cfg.daemon) cfg.daemon = {};
|
|
1456
|
-
cfg.daemon.
|
|
1456
|
+
if (!cfg.daemon.models) cfg.daemon.models = {};
|
|
1457
|
+
cfg.daemon.models.claude = 'opus';
|
|
1457
1458
|
writeConfigSafe(cfg);
|
|
1458
1459
|
config = loadConfig();
|
|
1459
1460
|
await bot.sendMessage(chatId, '✅ 模型已重置为 opus');
|
|
@@ -1480,9 +1481,10 @@ function createAdminCommandHandler(deps) {
|
|
|
1480
1481
|
issues++;
|
|
1481
1482
|
}
|
|
1482
1483
|
|
|
1483
|
-
const
|
|
1484
|
+
const daemonCfg = (cfg && cfg.daemon) || {};
|
|
1485
|
+
const m = resolveEngineModel('claude', daemonCfg);
|
|
1484
1486
|
const modelOk = isCustomProvider
|
|
1485
|
-
?
|
|
1487
|
+
? ['sonnet', 'opus', 'haiku'].includes(m)
|
|
1486
1488
|
: validModels.includes(m);
|
|
1487
1489
|
if (modelOk) {
|
|
1488
1490
|
checks.push(`✅ 模型: ${m}`);
|
|
@@ -1569,7 +1571,11 @@ function createAdminCommandHandler(deps) {
|
|
|
1569
1571
|
: engineCfg.provider;
|
|
1570
1572
|
const isBuiltinProvider = activeProvider === engineCfg.provider;
|
|
1571
1573
|
const distillModel = getDistillModel();
|
|
1572
|
-
const hintLine = engineCfg.hint
|
|
1574
|
+
const hintLine = engineCfg.hint
|
|
1575
|
+
? `\n💡 ${engineCfg.hint}`
|
|
1576
|
+
: (!isBuiltinProvider && currentEngine === 'claude'
|
|
1577
|
+
? `\n💡 ${activeProvider} 的后端真实模型请在 CC Switch / provider 层配置`
|
|
1578
|
+
: '');
|
|
1573
1579
|
|
|
1574
1580
|
if (!arg) {
|
|
1575
1581
|
const statusLine = `🤖 [${currentEngine}] 会话模型: ${currentModel} Provider: ${activeProvider}\n🧪 后台轻量: ${distillModel} (/distill-model 修改)${hintLine}`;
|
|
@@ -1587,10 +1593,13 @@ function createAdminCommandHandler(deps) {
|
|
|
1587
1593
|
}
|
|
1588
1594
|
|
|
1589
1595
|
const normalizedArg = arg.toLowerCase();
|
|
1590
|
-
//
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1596
|
+
// Claude session/config layer only accepts canonical slots; provider mapping stays in CC Switch.
|
|
1597
|
+
if (currentEngine === 'claude' && !optionValues.includes(normalizedArg)) {
|
|
1598
|
+
const suggested = normalizeClaudeModel(arg, '');
|
|
1599
|
+
const hint = suggested
|
|
1600
|
+
? `\n💡 检测到它更像 Claude 槽位 ${suggested},请直接用 /model ${suggested}`
|
|
1601
|
+
: '';
|
|
1602
|
+
await bot.sendMessage(chatId, `❌ Claude 会话模型只接受: ${optionValues.join(', ')}\n后端真实模型请在 CC Switch / provider 层配置,不要写进会话模型${hint}`);
|
|
1594
1603
|
return { handled: true, config };
|
|
1595
1604
|
}
|
|
1596
1605
|
|
|
@@ -434,13 +434,10 @@ function createClaudeEngine(deps) {
|
|
|
434
434
|
const entry = JSON.parse(line);
|
|
435
435
|
const sessionModel = entry && entry.message && entry.message.model;
|
|
436
436
|
if (!sessionModel || sessionModel === '<synthetic>') continue;
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
reason: 'non-claude-session',
|
|
442
|
-
};
|
|
443
|
-
}
|
|
437
|
+
// Custom Claude-compatible providers may record backend-native model ids
|
|
438
|
+
// (for example MiniMax) in the JSONL. Those sessions are still resumable;
|
|
439
|
+
// we only skip model pinning when the family cannot be mapped back to
|
|
440
|
+
// Claude's alias set.
|
|
444
441
|
// If the configured model is a short alias (sonnet/opus/haiku) and the JSONL model
|
|
445
442
|
// belongs to the same family, do NOT pin — let the alias resolve to the latest version.
|
|
446
443
|
// Only pin when the families genuinely differ (e.g. session was opus, config says sonnet).
|
|
@@ -1235,14 +1232,18 @@ function createClaudeEngine(deps) {
|
|
|
1235
1232
|
* Reset active provider back to anthropic/opus and reload config.
|
|
1236
1233
|
* Returns the freshly loaded config so callers can reassign their local variable.
|
|
1237
1234
|
*/
|
|
1238
|
-
function fallbackToDefaultProvider(reason) {
|
|
1235
|
+
function fallbackToDefaultProvider(reason, boundProjectKey = '') {
|
|
1239
1236
|
log('WARN', `Falling back to anthropic/opus — reason: ${reason}`);
|
|
1240
1237
|
if (providerMod && providerMod.getActiveName() !== 'anthropic') {
|
|
1241
1238
|
providerMod.setActive('anthropic');
|
|
1242
1239
|
}
|
|
1243
1240
|
const cfg = yaml.load(fs.readFileSync(CONFIG_FILE, 'utf8')) || {};
|
|
1244
1241
|
if (!cfg.daemon) cfg.daemon = {};
|
|
1245
|
-
cfg.daemon.
|
|
1242
|
+
if (!cfg.daemon.models) cfg.daemon.models = {};
|
|
1243
|
+
cfg.daemon.models.claude = 'opus';
|
|
1244
|
+
if (boundProjectKey && cfg.projects && cfg.projects[boundProjectKey]) {
|
|
1245
|
+
cfg.projects[boundProjectKey].model = 'opus';
|
|
1246
|
+
}
|
|
1246
1247
|
writeConfigSafe(cfg);
|
|
1247
1248
|
return loadConfig();
|
|
1248
1249
|
}
|
|
@@ -2269,7 +2270,7 @@ ${mentorRadarHint}
|
|
|
2269
2270
|
const looksLikeError = output.length < 300 && /\b(not found|invalid model|unauthorized|401|403|404|error|failed)\b/i.test(output);
|
|
2270
2271
|
if (looksLikeError && (activeProvCheck !== 'anthropic' || !builtinModelsCheck.includes(model))) {
|
|
2271
2272
|
try {
|
|
2272
|
-
config = fallbackToDefaultProvider(`output looks like error for ${activeProvCheck}/${model}
|
|
2273
|
+
config = fallbackToDefaultProvider(`output looks like error for ${activeProvCheck}/${model}`, boundProjectKey || '');
|
|
2273
2274
|
await bot.sendMessage(chatId, `⚠️ ${activeProvCheck}/${model} 疑似失败,已回退到 anthropic/opus\n输出: ${output.slice(0, 150)}`);
|
|
2274
2275
|
} catch (fbErr) {
|
|
2275
2276
|
log('ERROR', `Fallback failed: ${fbErr.message}`);
|
|
@@ -2500,7 +2501,7 @@ ${mentorRadarHint}
|
|
|
2500
2501
|
const builtinModelValues = (ENGINE_MODEL_CONFIG.claude.options || []).map(o => typeof o === 'string' ? o : o.value);
|
|
2501
2502
|
if ((activeProv !== 'anthropic' || !builtinModelValues.includes(model)) && !errMsg.includes('Stopped by user')) {
|
|
2502
2503
|
try {
|
|
2503
|
-
config = fallbackToDefaultProvider(`${activeProv}/${model} error: ${errMsg.slice(0, 100)}
|
|
2504
|
+
config = fallbackToDefaultProvider(`${activeProv}/${model} error: ${errMsg.slice(0, 100)}`, boundProjectKey || '');
|
|
2504
2505
|
await bot.sendMessage(chatId, `⚠️ ${activeProv}/${model} 失败,已回退到 anthropic/opus\n原因: ${errMsg.slice(0, 100)}`);
|
|
2505
2506
|
} catch (fallbackErr) {
|
|
2506
2507
|
log('ERROR', `Fallback failed: ${fallbackErr.message}`);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { resolveEngineModel } = require('./daemon-engine-runtime');
|
|
4
|
+
|
|
3
5
|
function createCommandRouter(deps) {
|
|
4
6
|
const {
|
|
5
7
|
loadState,
|
|
@@ -657,7 +659,7 @@ function createCommandRouter(deps) {
|
|
|
657
659
|
}
|
|
658
660
|
|
|
659
661
|
if (text.startsWith('/')) {
|
|
660
|
-
const currentModel = (config
|
|
662
|
+
const currentModel = resolveEngineModel('claude', (config && config.daemon) || {});
|
|
661
663
|
const currentProvider = providerMod ? providerMod.getActiveName() : 'anthropic';
|
|
662
664
|
await bot.sendMessage(chatId, [
|
|
663
665
|
'📱 手机端 Claude Code',
|
|
@@ -93,23 +93,58 @@ const BUILTIN_CLAUDE_MODEL_VALUES = Object.freeze(
|
|
|
93
93
|
).filter(Boolean)
|
|
94
94
|
);
|
|
95
95
|
|
|
96
|
+
function normalizeClaudeModel(model, fallback = ENGINE_MODEL_CONFIG.claude.main) {
|
|
97
|
+
const raw = String(model || '').trim();
|
|
98
|
+
if (!raw) return fallback;
|
|
99
|
+
const normalized = raw.toLowerCase();
|
|
100
|
+
if (BUILTIN_CLAUDE_MODEL_VALUES.includes(normalized)) return normalized;
|
|
101
|
+
if (normalized.includes('opus')) return 'opus';
|
|
102
|
+
if (normalized.includes('sonnet')) return 'sonnet';
|
|
103
|
+
if (normalized.includes('haiku')) return 'haiku';
|
|
104
|
+
return fallback;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function looksLikeCodexModel(model) {
|
|
108
|
+
const raw = String(model || '').trim().toLowerCase();
|
|
109
|
+
if (!raw) return false;
|
|
110
|
+
return (
|
|
111
|
+
raw.startsWith('gpt-')
|
|
112
|
+
|| raw.startsWith('o1')
|
|
113
|
+
|| raw.startsWith('o3')
|
|
114
|
+
|| raw.startsWith('o4')
|
|
115
|
+
|| raw.includes('codex')
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
96
119
|
function resolveEngineModel(engineName, daemonCfg = {}, overrideModel = '') {
|
|
97
120
|
const engine = normalizeEngineName(engineName);
|
|
98
121
|
const engineCfg = ENGINE_MODEL_CONFIG[engine] || ENGINE_MODEL_CONFIG.claude;
|
|
99
122
|
const engineModels = (daemonCfg && daemonCfg.models) || {};
|
|
100
123
|
const explicitModel = String(overrideModel || '').trim();
|
|
101
|
-
if (explicitModel)
|
|
124
|
+
if (explicitModel) {
|
|
125
|
+
return engine === 'claude'
|
|
126
|
+
? normalizeClaudeModel(explicitModel, engineCfg.main)
|
|
127
|
+
: explicitModel;
|
|
128
|
+
}
|
|
102
129
|
|
|
103
130
|
const perEngineModel = String(engineModels[engine] || '').trim();
|
|
104
|
-
if (perEngineModel)
|
|
131
|
+
if (perEngineModel) {
|
|
132
|
+
return engine === 'claude'
|
|
133
|
+
? normalizeClaudeModel(perEngineModel, engineCfg.main)
|
|
134
|
+
: perEngineModel;
|
|
135
|
+
}
|
|
105
136
|
|
|
106
137
|
const legacyModel = String((daemonCfg && daemonCfg.model) || '').trim();
|
|
107
138
|
if (!legacyModel) return engineCfg.main;
|
|
108
139
|
|
|
109
140
|
// Legacy daemon.model historically meant a Claude model.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
141
|
+
if (engine === 'claude') {
|
|
142
|
+
return normalizeClaudeModel(legacyModel, engineCfg.main);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Legacy daemon.model primarily belonged to Claude; only reuse it for Codex
|
|
146
|
+
// when it already looks like a real Codex/OpenAI model id.
|
|
147
|
+
if (engine === 'codex' && !looksLikeCodexModel(legacyModel)) {
|
|
113
148
|
return engineCfg.main;
|
|
114
149
|
}
|
|
115
150
|
return legacyModel;
|
|
@@ -420,6 +455,7 @@ module.exports = {
|
|
|
420
455
|
resolveBinary,
|
|
421
456
|
detectDefaultEngine,
|
|
422
457
|
resolveEngineModel,
|
|
458
|
+
normalizeClaudeModel,
|
|
423
459
|
ENGINE_MODEL_CONFIG,
|
|
424
460
|
ENGINE_DISTILL_MAP,
|
|
425
461
|
ENGINE_DEFAULT_MODEL,
|
|
@@ -434,5 +470,7 @@ module.exports = {
|
|
|
434
470
|
normalizeCodexApprovalPolicy,
|
|
435
471
|
resolveCodexPermissionProfile,
|
|
436
472
|
BUILTIN_CLAUDE_MODEL_VALUES,
|
|
473
|
+
normalizeClaudeModel,
|
|
474
|
+
looksLikeCodexModel,
|
|
437
475
|
},
|
|
438
476
|
};
|
|
@@ -296,7 +296,9 @@ function createSessionStore(deps) {
|
|
|
296
296
|
}
|
|
297
297
|
} catch {}
|
|
298
298
|
}
|
|
299
|
-
} catch {
|
|
299
|
+
} catch (err) {
|
|
300
|
+
log('WARN', `scanClaudeSessions project ${proj}: ${err.message}`);
|
|
301
|
+
}
|
|
300
302
|
|
|
301
303
|
try {
|
|
302
304
|
const files = fs.readdirSync(projDir).filter(f => f.endsWith('.jsonl'));
|
|
@@ -318,7 +320,9 @@ function createSessionStore(deps) {
|
|
|
318
320
|
});
|
|
319
321
|
}
|
|
320
322
|
}
|
|
321
|
-
} catch {
|
|
323
|
+
} catch (err) {
|
|
324
|
+
log('WARN', `scanClaudeSessions project ${proj}: ${err.message}`);
|
|
325
|
+
}
|
|
322
326
|
}
|
|
323
327
|
|
|
324
328
|
const all = Array.from(sessionMap.values()).map((entry) => ({ ...entry, engine: 'claude' }));
|
|
@@ -385,7 +389,8 @@ function createSessionStore(deps) {
|
|
|
385
389
|
} catch { /* non-fatal */ }
|
|
386
390
|
}
|
|
387
391
|
return all;
|
|
388
|
-
} catch {
|
|
392
|
+
} catch (err) {
|
|
393
|
+
log('WARN', `scanClaudeSessions: ${err.message}`);
|
|
389
394
|
return [];
|
|
390
395
|
}
|
|
391
396
|
}
|
|
@@ -444,8 +449,9 @@ function createSessionStore(deps) {
|
|
|
444
449
|
};
|
|
445
450
|
})
|
|
446
451
|
.map((session) => enrichCodexSession(session));
|
|
447
|
-
} catch {
|
|
452
|
+
} catch (err) {
|
|
448
453
|
if (db) { try { db.close(); } catch { /* ignore */ } }
|
|
454
|
+
log('WARN', `scanCodexSessions ${CODEX_DB}: ${err.message}`);
|
|
449
455
|
return [];
|
|
450
456
|
}
|
|
451
457
|
}
|
|
@@ -544,19 +550,15 @@ function createSessionStore(deps) {
|
|
|
544
550
|
|
|
545
551
|
function scanAllSessions() {
|
|
546
552
|
if (_sessionCache && (Date.now() - _sessionCacheTime < SESSION_CACHE_TTL)) return _sessionCache;
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
return all;
|
|
557
|
-
} catch {
|
|
558
|
-
return [];
|
|
559
|
-
}
|
|
553
|
+
const all = [...scanClaudeSessions(), ...scanCodexSessions()];
|
|
554
|
+
all.sort((a, b) => {
|
|
555
|
+
const aTime = a.fileMtime || new Date(a.modified).getTime();
|
|
556
|
+
const bTime = b.fileMtime || new Date(b.modified).getTime();
|
|
557
|
+
return bTime - aTime;
|
|
558
|
+
});
|
|
559
|
+
_sessionCache = all;
|
|
560
|
+
_sessionCacheTime = Date.now();
|
|
561
|
+
return all;
|
|
560
562
|
}
|
|
561
563
|
|
|
562
564
|
function listRecentSessions(limit, cwd, engine) {
|
|
@@ -1033,10 +1035,6 @@ function createSessionStore(deps) {
|
|
|
1033
1035
|
|
|
1034
1036
|
// Try to read cwd/model from session JSONL file content (most reliable)
|
|
1035
1037
|
const metadata = _readClaudeSessionMetadata(sessionFile);
|
|
1036
|
-
if (metadata.model && !metadata.model.startsWith('claude-')) {
|
|
1037
|
-
log('WARN', `[SessionValid] ${sessionId.slice(0, 8)}: non-claude model "${metadata.model}"`);
|
|
1038
|
-
return false;
|
|
1039
|
-
}
|
|
1040
1038
|
if (metadata.cwd && path.resolve(metadata.cwd) === normCwd) return true;
|
|
1041
1039
|
if (metadata.cwd) {
|
|
1042
1040
|
// CWD mismatch: the session was created for a different directory.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const { classifyTaskUsage } = require('./usage-classifier');
|
|
5
|
+
const { resolveEngineModel } = require('./daemon-engine-runtime');
|
|
5
6
|
|
|
6
7
|
const WEEKDAY_INDEX = Object.freeze({
|
|
7
8
|
sun: 0,
|
|
@@ -579,7 +580,7 @@ function createTaskScheduler(deps) {
|
|
|
579
580
|
if (steps.length === 0) return { success: false, error: 'No steps defined', output: '' };
|
|
580
581
|
|
|
581
582
|
// Workflow tasks match the user's session model setting (same quality as interactive)
|
|
582
|
-
const sessionModel = (
|
|
583
|
+
const sessionModel = resolveEngineModel('claude', (config && config.daemon) || {});
|
|
583
584
|
const model = normalizeModel(task.model || sessionModel);
|
|
584
585
|
const cwd = task.cwd ? task.cwd.replace(/^~/, HOME) : HOME;
|
|
585
586
|
const sessionId = crypto.randomUUID();
|
package/scripts/daemon.js
CHANGED
|
@@ -2078,16 +2078,6 @@ if (providerMod && typeof providerMod.setEngine === 'function') {
|
|
|
2078
2078
|
}
|
|
2079
2079
|
log('INFO', `Default engine: ${_defaultEngine} (detected: ${detectedEngine})`);
|
|
2080
2080
|
|
|
2081
|
-
// One-time migration: daemon.model (legacy) → daemon.models.<engine>
|
|
2082
|
-
try {
|
|
2083
|
-
const _migCfg = yaml.load(fs.readFileSync(CONFIG_FILE, 'utf8')) || {};
|
|
2084
|
-
if (_migCfg.daemon && _migCfg.daemon.model && !_migCfg.daemon.models) {
|
|
2085
|
-
_migCfg.daemon.models = { [_defaultEngine]: _migCfg.daemon.model };
|
|
2086
|
-
writeConfigSafe(_migCfg);
|
|
2087
|
-
log('INFO', `Migrated daemon.model="${_migCfg.daemon.model}" → daemon.models.${_defaultEngine}`);
|
|
2088
|
-
}
|
|
2089
|
-
} catch { /* ignore */ }
|
|
2090
|
-
|
|
2091
2081
|
function getDefaultEngine() {
|
|
2092
2082
|
return _defaultEngine;
|
|
2093
2083
|
}
|
|
@@ -2107,15 +2097,6 @@ function setDefaultEngine(engine) {
|
|
|
2107
2097
|
try { providerMod.setEngine(engine); } catch { /* ignore */ }
|
|
2108
2098
|
}
|
|
2109
2099
|
}
|
|
2110
|
-
// Migrate old daemon.model → daemon.models[engine] on first switch
|
|
2111
|
-
try {
|
|
2112
|
-
const cfg = yaml.load(fs.readFileSync(CONFIG_FILE, 'utf8')) || {};
|
|
2113
|
-
if (!cfg.daemon) cfg.daemon = {};
|
|
2114
|
-
if (cfg.daemon.model && !cfg.daemon.models) {
|
|
2115
|
-
cfg.daemon.models = { [engine]: cfg.daemon.model };
|
|
2116
|
-
writeConfigSafe(cfg);
|
|
2117
|
-
}
|
|
2118
|
-
} catch { /* ignore */ }
|
|
2119
2100
|
}
|
|
2120
2101
|
|
|
2121
2102
|
const getEngineRuntime = createEngineRuntimeFactory({
|
|
@@ -2609,10 +2590,6 @@ async function main() {
|
|
|
2609
2590
|
'enable_nl_mac_control',
|
|
2610
2591
|
'enable_nl_mac_fallback',
|
|
2611
2592
|
];
|
|
2612
|
-
// All known models across all engines (for legacy daemon.model validation only)
|
|
2613
|
-
const BUILTIN_CLAUDE_MODELS = (ENGINE_MODEL_CONFIG.claude.options || []).map(option =>
|
|
2614
|
-
typeof option === 'string' ? option : option.value
|
|
2615
|
-
).filter(Boolean);
|
|
2616
2593
|
for (const key of Object.keys(config)) {
|
|
2617
2594
|
if (!KNOWN_SECTIONS.includes(key)) log('WARN', `Config: unknown section "${key}" (typo?)`);
|
|
2618
2595
|
}
|
|
@@ -2620,14 +2597,11 @@ async function main() {
|
|
|
2620
2597
|
for (const key of Object.keys(config.daemon)) {
|
|
2621
2598
|
if (!KNOWN_DAEMON.includes(key)) log('WARN', `Config: unknown daemon.${key} (typo?)`);
|
|
2622
2599
|
}
|
|
2623
|
-
//
|
|
2624
|
-
|
|
2600
|
+
// Keep legacy daemon.model read-compatible, but never auto-migrate or treat it
|
|
2601
|
+
// as the write source of truth. The canonical writable field is daemon.models.<engine>.
|
|
2602
|
+
if (config.daemon.model) {
|
|
2625
2603
|
const activeProv = providerMod ? providerMod.getActiveName() : 'anthropic';
|
|
2626
|
-
|
|
2627
|
-
log('WARN', `Config: daemon.model="${config.daemon.model}" is not a known Claude model`);
|
|
2628
|
-
} else {
|
|
2629
|
-
log('INFO', `Config: legacy daemon.model="${config.daemon.model}" retained; active ${_defaultEngine} model resolves to "${resolveEngineModel(_defaultEngine, config.daemon)}" (${activeProv})`);
|
|
2630
|
-
}
|
|
2604
|
+
log('INFO', `Config: legacy daemon.model detected; active ${_defaultEngine} model resolves to "${resolveEngineModel(_defaultEngine, config.daemon)}" (${activeProv})`);
|
|
2631
2605
|
}
|
|
2632
2606
|
}
|
|
2633
2607
|
|