@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 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 use it by default. You can also choose from presets or specify a custom model.
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 model | Zero friction — works with what you have |
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 (Sonnet, Starter tier)
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
@@ -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 = Object.keys(config?.models?.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 special model + extra config
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
- const modelCfg = getModelConfig(preset, customModels, detectedModels);
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: preset,
708
+ modelPreset: effectivePreset,
706
709
  });
707
710
  printStepDone('Manifest written');
708
711
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robbiesrobotics/alice-agents",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "A.L.I.C.E. — 28 AI agents for OpenClaw. One conversation, one team.",
5
5
  "bin": {
6
6
  "alice-agents": "bin/alice-install.mjs"
@@ -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