@robbiesrobotics/alice-agents 1.2.8 → 1.2.9
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/LICENSE +21 -0
- package/README.md +12 -5
- package/lib/config-merger.mjs +68 -10
- package/lib/installer.mjs +29 -6
- package/lib/prompter.mjs +16 -6
- package/package.json +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Robbies Robotics (av3.ai)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -51,16 +51,23 @@ An orchestrator (Olivia) backed by specialist agents across every domain:
|
|
|
51
51
|
| **Eva** | Executive Assistant | 📌 | Pro |
|
|
52
52
|
| **Parker** | Project Management | 📅 | Pro |
|
|
53
53
|
|
|
54
|
-
## Model
|
|
54
|
+
## Model Flexibility
|
|
55
|
+
|
|
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
|
+
|
|
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.
|
|
55
59
|
|
|
56
60
|
| Preset | Models | Best For |
|
|
57
61
|
|--------|--------|----------|
|
|
58
|
-
| **
|
|
59
|
-
| **
|
|
62
|
+
| **Auto-detect** (default) | Your current OpenClaw model | Zero friction — works with what you have |
|
|
63
|
+
| **Sonnet** | claude-sonnet-4-6 for all | Balanced speed + quality (requires Anthropic key) |
|
|
64
|
+
| **Opus + Sonnet** | Opus for orchestrator, Sonnet for specialists | Maximum quality (requires Anthropic key) |
|
|
60
65
|
| **OpenAI** | GPT-5.4 / GPT-5.4-mini | OpenAI users |
|
|
61
|
-
| **Local (Ollama)** | Local models | Privacy, offline use |
|
|
66
|
+
| **Local (Ollama)** | Local models | Privacy, offline use, no API key needed |
|
|
62
67
|
| **Custom** | Your choice | Full control |
|
|
63
68
|
|
|
69
|
+
> **Tip:** We prefer Claude Opus for orchestration when available — but A.L.I.C.E. is model-agnostic. Whatever model OpenClaw has configured will work.
|
|
70
|
+
|
|
64
71
|
## Install Options
|
|
65
72
|
|
|
66
73
|
```bash
|
|
@@ -132,7 +139,7 @@ If you're already running NemoClaw, A.L.I.C.E. works out of the box — no extra
|
|
|
132
139
|
|
|
133
140
|
- [OpenClaw](https://openclaw.ai) or [NemoClaw](https://nemoclaw.com) installed and configured
|
|
134
141
|
- Node.js 18+
|
|
135
|
-
- At least one AI provider configured (Anthropic, OpenAI, or
|
|
142
|
+
- At least one AI provider configured in OpenClaw (Anthropic, OpenAI, Ollama, or any other supported provider)
|
|
136
143
|
|
|
137
144
|
## License
|
|
138
145
|
|
package/lib/config-merger.mjs
CHANGED
|
@@ -15,6 +15,43 @@ export function readConfig() {
|
|
|
15
15
|
return JSON.parse(raw);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Inspect the existing OpenClaw/NemoClaw config and return what models
|
|
20
|
+
* and providers the user already has set up. This is used to avoid
|
|
21
|
+
* forcing Anthropic/Claude on users who haven't configured those keys.
|
|
22
|
+
*
|
|
23
|
+
* Returns null if config can't be read.
|
|
24
|
+
*/
|
|
25
|
+
export function detectAvailableModels() {
|
|
26
|
+
if (!configExists()) return null;
|
|
27
|
+
try {
|
|
28
|
+
const config = readConfig();
|
|
29
|
+
|
|
30
|
+
// Current agents.defaults.model (set by a prior install or manually)
|
|
31
|
+
const agentDefaults = config?.agents?.defaults?.model || {};
|
|
32
|
+
|
|
33
|
+
// Top-level model override (some users set this)
|
|
34
|
+
const globalModel = config?.model;
|
|
35
|
+
|
|
36
|
+
// Configured provider keys (e.g. ['anthropic', 'openai', 'ollama'])
|
|
37
|
+
const providers = Object.keys(config?.models?.providers || {});
|
|
38
|
+
|
|
39
|
+
const primary = agentDefaults.primary || globalModel || null;
|
|
40
|
+
const orchestrator = agentDefaults.orchestrator || primary;
|
|
41
|
+
const fallbacks = agentDefaults.fallbacks || [];
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
primary,
|
|
45
|
+
orchestrator,
|
|
46
|
+
fallbacks,
|
|
47
|
+
providers,
|
|
48
|
+
hasModel: !!primary,
|
|
49
|
+
};
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
18
55
|
function backupConfig() {
|
|
19
56
|
const ts = Date.now();
|
|
20
57
|
const backupPath = join(OPENCLAW_DIR, `openclaw.json.bak.alice-${ts}`);
|
|
@@ -29,8 +66,17 @@ function writeConfigAtomic(config) {
|
|
|
29
66
|
renameSync(tmpPath, CONFIG_PATH);
|
|
30
67
|
}
|
|
31
68
|
|
|
32
|
-
function getModelConfig(preset, customModels) {
|
|
69
|
+
function getModelConfig(preset, customModels, detectedModels) {
|
|
33
70
|
switch (preset) {
|
|
71
|
+
case 'detected':
|
|
72
|
+
// Use whatever the user already has configured — don't touch model settings at all
|
|
73
|
+
return {
|
|
74
|
+
primary: detectedModels?.primary || null,
|
|
75
|
+
orchestrator: detectedModels?.orchestrator || detectedModels?.primary || null,
|
|
76
|
+
fallbacks: detectedModels?.fallbacks || [],
|
|
77
|
+
models: {},
|
|
78
|
+
preserve: true, // signal: don't overwrite model defaults
|
|
79
|
+
};
|
|
34
80
|
case 'sonnet':
|
|
35
81
|
return {
|
|
36
82
|
primary: 'anthropic/claude-sonnet-4-6',
|
|
@@ -71,8 +117,8 @@ function getModelConfig(preset, customModels) {
|
|
|
71
117
|
};
|
|
72
118
|
case 'custom':
|
|
73
119
|
return {
|
|
74
|
-
primary: customModels?.primary ||
|
|
75
|
-
orchestrator: customModels?.orchestrator || customModels?.primary ||
|
|
120
|
+
primary: customModels?.primary || null,
|
|
121
|
+
orchestrator: customModels?.orchestrator || customModels?.primary || null,
|
|
76
122
|
fallbacks: [],
|
|
77
123
|
models: {},
|
|
78
124
|
};
|
|
@@ -110,7 +156,8 @@ function buildAgentEntry(agent, modelCfg) {
|
|
|
110
156
|
export function mergeConfig({ agents, mode, preset, customModels }) {
|
|
111
157
|
const backupPath = backupConfig();
|
|
112
158
|
const config = readConfig();
|
|
113
|
-
const
|
|
159
|
+
const detectedModels = detectAvailableModels();
|
|
160
|
+
const modelCfg = getModelConfig(preset, customModels, detectedModels);
|
|
114
161
|
|
|
115
162
|
// Build agent entries
|
|
116
163
|
const aliceEntries = agents.map((a) => buildAgentEntry(a, modelCfg));
|
|
@@ -140,13 +187,24 @@ export function mergeConfig({ agents, mode, preset, customModels }) {
|
|
|
140
187
|
|
|
141
188
|
// Set defaults
|
|
142
189
|
config.agents.defaults = config.agents.defaults || {};
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
190
|
+
|
|
191
|
+
if (modelCfg.preserve) {
|
|
192
|
+
// User's existing model config is intact — don't overwrite it.
|
|
193
|
+
// Only initialize the key if it doesn't exist at all (safety net).
|
|
194
|
+
config.agents.defaults.model = config.agents.defaults.model || {};
|
|
195
|
+
} else if (modelCfg.primary) {
|
|
196
|
+
config.agents.defaults.model = {
|
|
197
|
+
primary: modelCfg.primary,
|
|
198
|
+
fallbacks: modelCfg.fallbacks,
|
|
199
|
+
};
|
|
200
|
+
if (modelCfg.orchestrator && modelCfg.orchestrator !== modelCfg.primary) {
|
|
201
|
+
config.agents.defaults.model.orchestrator = modelCfg.orchestrator;
|
|
202
|
+
}
|
|
203
|
+
if (Object.keys(modelCfg.models).length > 0) {
|
|
204
|
+
config.agents.defaults.models = modelCfg.models;
|
|
205
|
+
}
|
|
149
206
|
}
|
|
207
|
+
// If modelCfg.primary is null (custom/detected with nothing set), leave existing model config alone
|
|
150
208
|
config.agents.defaults.workspace = join(OPENCLAW_DIR, 'workspace-olivia');
|
|
151
209
|
config.agents.defaults.compaction = config.agents.defaults.compaction || { mode: 'safeguard' };
|
|
152
210
|
config.agents.defaults.maxConcurrent = config.agents.defaults.maxConcurrent || 4;
|
package/lib/installer.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { readFileSync, existsSync } from 'node:fs';
|
|
|
2
2
|
import { join, dirname } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { execSync } from 'node:child_process';
|
|
5
|
-
import { configExists, mergeConfig, removeAliceAgents } from './config-merger.mjs';
|
|
5
|
+
import { configExists, mergeConfig, removeAliceAgents, detectAvailableModels } from './config-merger.mjs';
|
|
6
6
|
import { scaffoldAll } from './workspace-scaffolder.mjs';
|
|
7
7
|
import { readManifest, writeManifest } from './manifest.mjs';
|
|
8
8
|
import {
|
|
@@ -170,11 +170,18 @@ function printBanner() {
|
|
|
170
170
|
console.log();
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
function printSummary(mode, tier, agents, preset, userInfo) {
|
|
173
|
+
function printSummary(mode, tier, agents, preset, userInfo, detectedModels) {
|
|
174
|
+
const modelLabel =
|
|
175
|
+
preset === 'detected'
|
|
176
|
+
? `${detectedModels?.primary || 'your configured model'} (detected)`
|
|
177
|
+
: preset === 'custom'
|
|
178
|
+
? `custom`
|
|
179
|
+
: preset;
|
|
180
|
+
|
|
174
181
|
console.log('\n ── Install Summary ──────────────────────────────');
|
|
175
182
|
console.log(` Mode: ${mode}`);
|
|
176
183
|
console.log(` Tier: ${tier} (${agents.length} agents)`);
|
|
177
|
-
console.log(` Model: ${
|
|
184
|
+
console.log(` Model: ${modelLabel}`);
|
|
178
185
|
console.log(` User: ${userInfo.name}`);
|
|
179
186
|
console.log(` Timezone: ${userInfo.timezone}`);
|
|
180
187
|
console.log(' ─────────────────────────────────────────────────');
|
|
@@ -229,6 +236,19 @@ export async function runInstall(options = {}) {
|
|
|
229
236
|
console.log(' 💡 Tip: Upgrade to NemoClaw for enterprise security: https://nvidia.com/nemoclaw\n');
|
|
230
237
|
}
|
|
231
238
|
|
|
239
|
+
// Detect what models the user already has configured
|
|
240
|
+
const detectedModels = detectAvailableModels();
|
|
241
|
+
if (detectedModels?.hasModel) {
|
|
242
|
+
console.log(` ✔ Detected configured model: ${detectedModels.primary}`);
|
|
243
|
+
if (detectedModels.providers.length > 0) {
|
|
244
|
+
console.log(` Providers: ${detectedModels.providers.join(', ')}\n`);
|
|
245
|
+
} else {
|
|
246
|
+
console.log();
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
console.log(' ℹ No model configured yet — you\'ll be prompted to choose one.\n');
|
|
250
|
+
}
|
|
251
|
+
|
|
232
252
|
const allAgents = loadAgentRegistry();
|
|
233
253
|
|
|
234
254
|
// 2. Install mode
|
|
@@ -269,9 +289,12 @@ export async function runInstall(options = {}) {
|
|
|
269
289
|
// 4. Model preset
|
|
270
290
|
let preset, customModels;
|
|
271
291
|
if (auto) {
|
|
272
|
-
|
|
292
|
+
// Non-interactive: use whatever the user already has configured.
|
|
293
|
+
// Only fall back to sonnet if nothing is detected (e.g. fresh OpenClaw install
|
|
294
|
+
// where the user explicitly set up Claude credentials).
|
|
295
|
+
preset = detectedModels?.hasModel ? 'detected' : 'sonnet';
|
|
273
296
|
} else {
|
|
274
|
-
preset = await promptModelPreset();
|
|
297
|
+
preset = await promptModelPreset(detectedModels);
|
|
275
298
|
if (preset === 'custom') {
|
|
276
299
|
customModels = await promptCustomModel();
|
|
277
300
|
}
|
|
@@ -307,7 +330,7 @@ export async function runInstall(options = {}) {
|
|
|
307
330
|
const agents = allAgents;
|
|
308
331
|
|
|
309
332
|
// 6. Confirmation
|
|
310
|
-
printSummary(mode, tier, agents, preset, userInfo);
|
|
333
|
+
printSummary(mode, tier, agents, preset, userInfo, detectedModels);
|
|
311
334
|
|
|
312
335
|
if (!auto) {
|
|
313
336
|
const ok = await confirm(' Proceed with installation?');
|
package/lib/prompter.mjs
CHANGED
|
@@ -78,19 +78,29 @@ export async function promptUserInfo() {
|
|
|
78
78
|
return { name, timezone: tz, notes };
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
export async function promptModelPreset() {
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
export async function promptModelPreset(detectedModels) {
|
|
82
|
+
const options = [];
|
|
83
|
+
|
|
84
|
+
// If the user already has a model configured, put it first
|
|
85
|
+
if (detectedModels?.hasModel) {
|
|
86
|
+
const label = `Use your current model (${detectedModels.primary}) — recommended`;
|
|
87
|
+
options.push({ label, value: 'detected' });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
options.push(
|
|
91
|
+
{ label: 'Sonnet — claude-sonnet-4-6 for all agents', value: 'sonnet' },
|
|
84
92
|
{ label: 'Opus + Sonnet — Opus for orchestrator, Sonnet for specialists', value: 'opus-sonnet' },
|
|
85
93
|
{ label: 'OpenAI — GPT-5.4 / GPT-5.4-mini', value: 'openai' },
|
|
86
94
|
{ label: 'Local (Ollama) — requires local models', value: 'local' },
|
|
87
95
|
{ label: 'Custom — specify your own model strings', value: 'custom' },
|
|
88
|
-
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return choose('\n Model selection:', options);
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
export async function promptCustomModel() {
|
|
92
|
-
const primary = await input('Primary model (e.g., anthropic/claude-sonnet-4-6)');
|
|
93
|
-
const orchestrator = await input('Orchestrator model (or same as primary)', primary);
|
|
102
|
+
const primary = await input(' Primary model (e.g., anthropic/claude-sonnet-4-6, openai/gpt-4o, ollama/llama3)');
|
|
103
|
+
const orchestrator = await input(' Orchestrator model (or same as primary)', primary);
|
|
94
104
|
return { primary, orchestrator };
|
|
95
105
|
}
|
|
96
106
|
|