@robbiesrobotics/alice-agents 1.4.0 → 1.4.1

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
@@ -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,
@@ -157,7 +200,17 @@ export function mergeConfig({ agents, mode, preset, customModels }) {
157
200
  const backupPath = backupConfig();
158
201
  const config = readConfig();
159
202
  const detectedModels = detectAvailableModels();
160
- const modelCfg = getModelConfig(preset, customModels, detectedModels);
203
+ let effectivePreset = preset;
204
+ let warning = null;
205
+ let modelCfg = getModelConfig(preset, customModels, detectedModels);
206
+
207
+ if (!modelCfg.preserve && !isCompatibleWithConfiguredProviders(modelCfg, detectedModels)) {
208
+ effectivePreset = detectedModels?.hasModel ? 'detected' : preset;
209
+ if (effectivePreset === 'detected') {
210
+ warning = `Preset "${preset}" does not match configured OpenClaw providers (${(detectedModels.providers || []).join(', ')}). Preserving existing model settings instead.`;
211
+ modelCfg = getModelConfig('detected', customModels, detectedModels);
212
+ }
213
+ }
161
214
 
162
215
  // Build agent entries
163
216
  const aliceEntries = agents.map((a) => buildAgentEntry(a, modelCfg));
@@ -221,7 +274,7 @@ export function mergeConfig({ agents, mode, preset, customModels }) {
221
274
  config.tools.agentToAgent.allow = [...aliceIds];
222
275
 
223
276
  writeConfigAtomic(config);
224
- return { backupPath, agentCount: aliceEntries.length };
277
+ return { backupPath, agentCount: aliceEntries.length, effectivePreset, warning };
225
278
  }
226
279
 
227
280
  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.1",
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