@robbiesrobotics/alice-agents 1.4.0 → 1.4.2
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/README.md +3 -3
- package/lib/config-merger.mjs +59 -5
- package/lib/installer.mjs +5 -2
- package/package.json +1 -1
- package/tools/compatibility-checker.mjs +27 -0
package/README.md
CHANGED
|
@@ -55,11 +55,11 @@ An orchestrator (Olivia) backed by specialist agents across every domain:
|
|
|
55
55
|
|
|
56
56
|
A.L.I.C.E. works with **whatever model you already have configured** in OpenClaw/NemoClaw. There's no required API key — just use what you've got.
|
|
57
57
|
|
|
58
|
-
When you install, the installer will **auto-detect your configured model** and
|
|
58
|
+
When you install, the installer will **auto-detect your configured model** and let the agent team inherit that runtime default. You can also choose from presets or specify a custom model for the OpenClaw/NemoClaw defaults A.L.I.C.E. should use.
|
|
59
59
|
|
|
60
60
|
| Preset | Models | Best For |
|
|
61
61
|
|--------|--------|----------|
|
|
62
|
-
| **Auto-detect** (default) | Your current OpenClaw
|
|
62
|
+
| **Auto-detect** (default) | Your current OpenClaw/NemoClaw default | Zero friction — agents inherit what your runtime already uses |
|
|
63
63
|
| **Sonnet** | claude-sonnet-4-6 for all | Balanced speed + quality (requires Anthropic key) |
|
|
64
64
|
| **Opus + Sonnet** | Opus for orchestrator, Sonnet for specialists | Maximum quality (requires Anthropic key) |
|
|
65
65
|
| **OpenAI** | GPT-5.4 / GPT-5.4-mini | OpenAI users |
|
|
@@ -74,7 +74,7 @@ When you install, the installer will **auto-detect your configured model** and u
|
|
|
74
74
|
# Interactive install
|
|
75
75
|
npx @robbiesrobotics/alice-agents
|
|
76
76
|
|
|
77
|
-
# Non-interactive with defaults (
|
|
77
|
+
# Non-interactive with defaults (detected model if available, otherwise Sonnet; Starter tier)
|
|
78
78
|
npx @robbiesrobotics/alice-agents --yes
|
|
79
79
|
|
|
80
80
|
# Show help
|
package/lib/config-merger.mjs
CHANGED
|
@@ -6,6 +6,33 @@ import { randomBytes } from 'node:crypto';
|
|
|
6
6
|
const OPENCLAW_DIR = join(homedir(), '.openclaw');
|
|
7
7
|
const CONFIG_PATH = join(OPENCLAW_DIR, 'openclaw.json');
|
|
8
8
|
|
|
9
|
+
function normalizeProviderId(provider) {
|
|
10
|
+
if (!provider) return null;
|
|
11
|
+
if (provider === 'openai-codex') return 'openai';
|
|
12
|
+
return provider;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getModelProvider(model) {
|
|
16
|
+
if (!model || typeof model !== 'string' || !model.includes('/')) return null;
|
|
17
|
+
return normalizeProviderId(model.split('/')[0]);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function collectConfiguredProviders(config) {
|
|
21
|
+
const providers = new Set();
|
|
22
|
+
|
|
23
|
+
for (const provider of Object.keys(config?.models?.providers || {})) {
|
|
24
|
+
const normalized = normalizeProviderId(provider);
|
|
25
|
+
if (normalized) providers.add(normalized);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
for (const profile of Object.values(config?.auth?.profiles || {})) {
|
|
29
|
+
const normalized = normalizeProviderId(profile?.provider);
|
|
30
|
+
if (normalized) providers.add(normalized);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return [...providers];
|
|
34
|
+
}
|
|
35
|
+
|
|
9
36
|
export function configExists() {
|
|
10
37
|
return existsSync(CONFIG_PATH);
|
|
11
38
|
}
|
|
@@ -34,7 +61,7 @@ export function detectAvailableModels() {
|
|
|
34
61
|
const globalModel = config?.model;
|
|
35
62
|
|
|
36
63
|
// Configured provider keys (e.g. ['anthropic', 'openai', 'ollama'])
|
|
37
|
-
const providers =
|
|
64
|
+
const providers = collectConfiguredProviders(config);
|
|
38
65
|
|
|
39
66
|
const primary = agentDefaults.primary || globalModel || null;
|
|
40
67
|
const orchestrator = agentDefaults.orchestrator || primary;
|
|
@@ -127,6 +154,22 @@ function getModelConfig(preset, customModels, detectedModels) {
|
|
|
127
154
|
}
|
|
128
155
|
}
|
|
129
156
|
|
|
157
|
+
function isCompatibleWithConfiguredProviders(modelCfg, detectedModels) {
|
|
158
|
+
const providers = new Set(detectedModels?.providers || []);
|
|
159
|
+
if (providers.size === 0) return true;
|
|
160
|
+
|
|
161
|
+
const models = [
|
|
162
|
+
modelCfg?.primary,
|
|
163
|
+
modelCfg?.orchestrator,
|
|
164
|
+
...(modelCfg?.fallbacks || []),
|
|
165
|
+
].filter(Boolean);
|
|
166
|
+
|
|
167
|
+
return models.every((model) => {
|
|
168
|
+
const provider = getModelProvider(model);
|
|
169
|
+
return !provider || providers.has(provider);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
130
173
|
function buildAgentEntry(agent, modelCfg) {
|
|
131
174
|
const entry = {
|
|
132
175
|
id: agent.id,
|
|
@@ -141,10 +184,11 @@ function buildAgentEntry(agent, modelCfg) {
|
|
|
141
184
|
tools: agent.tools,
|
|
142
185
|
};
|
|
143
186
|
|
|
144
|
-
// Orchestrator gets
|
|
187
|
+
// Orchestrator gets default-agent behavior, but no explicit model pin.
|
|
188
|
+
// This keeps Olivia model-agnostic and lets OpenClaw/NemoClaw route her
|
|
189
|
+
// through whatever default model/provider the runtime is configured to use.
|
|
145
190
|
if (agent.isOrchestrator) {
|
|
146
191
|
entry.default = true;
|
|
147
|
-
entry.model = modelCfg.orchestrator;
|
|
148
192
|
if (agent.extraConfig) {
|
|
149
193
|
Object.assign(entry, agent.extraConfig);
|
|
150
194
|
}
|
|
@@ -157,7 +201,17 @@ export function mergeConfig({ agents, mode, preset, customModels }) {
|
|
|
157
201
|
const backupPath = backupConfig();
|
|
158
202
|
const config = readConfig();
|
|
159
203
|
const detectedModels = detectAvailableModels();
|
|
160
|
-
|
|
204
|
+
let effectivePreset = preset;
|
|
205
|
+
let warning = null;
|
|
206
|
+
let modelCfg = getModelConfig(preset, customModels, detectedModels);
|
|
207
|
+
|
|
208
|
+
if (!modelCfg.preserve && !isCompatibleWithConfiguredProviders(modelCfg, detectedModels)) {
|
|
209
|
+
effectivePreset = detectedModels?.hasModel ? 'detected' : preset;
|
|
210
|
+
if (effectivePreset === 'detected') {
|
|
211
|
+
warning = `Preset "${preset}" does not match configured OpenClaw providers (${(detectedModels.providers || []).join(', ')}). Preserving existing model settings instead.`;
|
|
212
|
+
modelCfg = getModelConfig('detected', customModels, detectedModels);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
161
215
|
|
|
162
216
|
// Build agent entries
|
|
163
217
|
const aliceEntries = agents.map((a) => buildAgentEntry(a, modelCfg));
|
|
@@ -221,7 +275,7 @@ export function mergeConfig({ agents, mode, preset, customModels }) {
|
|
|
221
275
|
config.tools.agentToAgent.allow = [...aliceIds];
|
|
222
276
|
|
|
223
277
|
writeConfigAtomic(config);
|
|
224
|
-
return { backupPath, agentCount: aliceEntries.length };
|
|
278
|
+
return { backupPath, agentCount: aliceEntries.length, effectivePreset, warning };
|
|
225
279
|
}
|
|
226
280
|
|
|
227
281
|
export function removeAliceAgents(agentIds) {
|
package/lib/installer.mjs
CHANGED
|
@@ -665,13 +665,16 @@ export async function runInstall(options = {}) {
|
|
|
665
665
|
console.log('');
|
|
666
666
|
|
|
667
667
|
// Merge config
|
|
668
|
-
const { backupPath, agentCount } = mergeConfig({
|
|
668
|
+
const { backupPath, agentCount, effectivePreset, warning } = mergeConfig({
|
|
669
669
|
agents,
|
|
670
670
|
mode,
|
|
671
671
|
preset,
|
|
672
672
|
customModels,
|
|
673
673
|
});
|
|
674
674
|
printStepDone('Config updated', `backup: ${backupPath}`);
|
|
675
|
+
if (warning) {
|
|
676
|
+
console.log(` ${icons.warn} ${yellow(warning)}`);
|
|
677
|
+
}
|
|
675
678
|
|
|
676
679
|
// Scaffold workspaces
|
|
677
680
|
const results = scaffoldAll(agents, userInfo);
|
|
@@ -702,7 +705,7 @@ export async function runInstall(options = {}) {
|
|
|
702
705
|
agents: agents.map((a) => a.id),
|
|
703
706
|
userName: userInfo.name,
|
|
704
707
|
userTimezone: userInfo.timezone,
|
|
705
|
-
modelPreset:
|
|
708
|
+
modelPreset: effectivePreset,
|
|
706
709
|
});
|
|
707
710
|
printStepDone('Manifest written');
|
|
708
711
|
|
package/package.json
CHANGED
|
@@ -89,6 +89,33 @@ function diffConfigSchema(snapshot, liveConfig) {
|
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
const configuredProviders = new Set([
|
|
93
|
+
...Object.keys(liveConfig?.models?.providers || {}),
|
|
94
|
+
...Object.values(liveConfig?.auth?.profiles || {}).map((profile) => profile?.provider),
|
|
95
|
+
].filter(Boolean).map((provider) => provider === 'openai-codex' ? 'openai' : provider));
|
|
96
|
+
|
|
97
|
+
const referencedModels = [
|
|
98
|
+
defaults?.model?.primary,
|
|
99
|
+
defaults?.model?.orchestrator,
|
|
100
|
+
...(defaults?.model?.fallbacks || []),
|
|
101
|
+
...agents.map((agent) => agent?.model).filter(Boolean),
|
|
102
|
+
].filter(Boolean);
|
|
103
|
+
|
|
104
|
+
for (const model of referencedModels) {
|
|
105
|
+
const provider = typeof model === 'string' && model.includes('/') ? model.split('/')[0] : null;
|
|
106
|
+
const normalized = provider === 'openai-codex' ? 'openai' : provider;
|
|
107
|
+
if (normalized && configuredProviders.size > 0 && !configuredProviders.has(normalized)) {
|
|
108
|
+
changes.push({
|
|
109
|
+
category: 'config',
|
|
110
|
+
severity: 'high',
|
|
111
|
+
field: 'agents.defaults.model',
|
|
112
|
+
change: `Model '${model}' references provider '${normalized}', but that provider is not configured in OpenClaw auth/models`,
|
|
113
|
+
autoFixable: false
|
|
114
|
+
});
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
92
119
|
return changes;
|
|
93
120
|
}
|
|
94
121
|
|