v2.9.0 — Settings overhaul, Mistral parallel tool fix, Anthropic provider

- Fix settings save bug: primaryModel captured before _modelSettingsLoaded guard → wiped model roles when models tab not opened
- Fix server: filter empty-string roles before saving to prevent role wipe
- Add Voice and Session settings tabs to UI
- Add OpenWeather API key field to Credentials tab
- Add GET/POST endpoints: /api/settings/voice, /api/settings/session, /api/settings/openweather
- Fix Mistral parallel tool calls: model embeds tool calls as text instead of tool_calls array; parseEmbeddedToolCalls() detects and converts
- Add Anthropic provider adapter (anthropic-adapter.ts)
- Weather tool vault key resolution fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
kim
2026-05-23 18:52:40 +09:00
co-authored by Claude Sonnet 4.6
parent eaf6678a21
commit 8aee152f2f
12 changed files with 1226 additions and 157 deletions
+13 -1
View File
@@ -2,6 +2,7 @@ import fs from 'fs';
import path from 'path';
import os from 'os';
import { ToolResult } from '../types.js';
import { getVault } from '../security/vault.js';
const OW_BASE = 'https://api.openweathermap.org/data/2.5';
@@ -11,9 +12,20 @@ function getApiKey(): string | undefined {
const cfgPath = fs.existsSync(projectCfg)
? projectCfg
: path.join(os.homedir(), '.smallclaw', 'config.json');
const configDir = path.dirname(cfgPath);
if (fs.existsSync(cfgPath)) {
const data = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
return data.openweather?.api_key || data.weather?.api_key;
let raw = data.openweather?.api_key || data.weather?.api_key;
// Resolve vault reference (e.g. "vault:openweather.api_key")
if (typeof raw === 'string' && raw.startsWith('vault:')) {
const vaultKey = raw.slice('vault:'.length);
const entry = getVault(configDir).get(vaultKey, 'weather:getkey');
return entry?.expose();
}
if (raw) return raw;
// Key may be in vault without a config reference
const entry = getVault(configDir).get('openweather.api_key', 'weather:getkey');
return entry?.expose();
}
} catch {}
return undefined;