@robbiesrobotics/alice-agents 1.1.0 → 1.2.0
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/SELF-HEALING-SPEC.md +2503 -0
- package/lib/installer.mjs +78 -1
- package/package.json +4 -1
- package/snapshots/schema-snapshot.json +221 -0
- package/snapshots/tool-snapshot.json +37 -0
- package/tools/compatibility-checker.mjs +268 -0
- package/tools/local-remediation.mjs +231 -0
package/lib/installer.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
1
|
+
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';
|
|
@@ -27,6 +27,63 @@ function isOpenClawInstalled() {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function getOpenClawVersion() {
|
|
31
|
+
try {
|
|
32
|
+
const output = execSync('openclaw --version', { stdio: 'pipe', encoding: 'utf8' });
|
|
33
|
+
const match = output.match(/(\d{4}\.\d+\.\d+)/);
|
|
34
|
+
return match ? match[1] : null;
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getLatestNpmVersion() {
|
|
41
|
+
try {
|
|
42
|
+
const output = execSync('npm view openclaw version', { stdio: 'pipe', encoding: 'utf8' });
|
|
43
|
+
return output.trim();
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function checkForOpenClawUpdate(auto) {
|
|
50
|
+
const current = getOpenClawVersion();
|
|
51
|
+
if (!current) return;
|
|
52
|
+
|
|
53
|
+
console.log(` OpenClaw version: ${current}`);
|
|
54
|
+
|
|
55
|
+
const latest = getLatestNpmVersion();
|
|
56
|
+
if (!latest) {
|
|
57
|
+
console.log(' ⚠️ Could not check for updates (npm registry unreachable)\n');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (current === latest) {
|
|
62
|
+
console.log(` ✓ OpenClaw is up to date (${latest})\n`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(` ⬆️ Update available: ${current} → ${latest}\n`);
|
|
67
|
+
|
|
68
|
+
let shouldUpdate = auto;
|
|
69
|
+
if (!auto) {
|
|
70
|
+
shouldUpdate = await confirm(' Update OpenClaw to latest before continuing?');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (shouldUpdate) {
|
|
74
|
+
console.log(' 📦 Updating OpenClaw...\n');
|
|
75
|
+
try {
|
|
76
|
+
execSync('npm install -g openclaw@latest', { stdio: 'inherit' });
|
|
77
|
+
const updated = getOpenClawVersion();
|
|
78
|
+
console.log(`\n ✓ OpenClaw updated to ${updated || latest}\n`);
|
|
79
|
+
} catch {
|
|
80
|
+
console.log('\n ⚠️ Update failed — continuing with current version\n');
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
console.log();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
30
87
|
async function installOpenClaw(auto) {
|
|
31
88
|
console.log(' ⚠️ OpenClaw is not installed.\n');
|
|
32
89
|
|
|
@@ -127,6 +184,22 @@ function printSummary(mode, tier, agents, preset, userInfo) {
|
|
|
127
184
|
export async function runInstall(options = {}) {
|
|
128
185
|
const auto = options.yes || false;
|
|
129
186
|
|
|
187
|
+
// Check health flag first (before banner)
|
|
188
|
+
if (process.argv.includes('--health')) {
|
|
189
|
+
const healthPath = join(process.env.HOME, '.openclaw', '.alice-health-alert.json');
|
|
190
|
+
if (existsSync(healthPath)) {
|
|
191
|
+
const alerts = JSON.parse(readFileSync(healthPath, 'utf8'));
|
|
192
|
+
console.log('\n⚠️ A.L.I.C.E. Health Report\n');
|
|
193
|
+
for (const alert of alerts.alerts) {
|
|
194
|
+
console.log(` [${alert.severity}] ${alert.category}: ${alert.field}`);
|
|
195
|
+
console.log(` ${alert.description}\n`);
|
|
196
|
+
}
|
|
197
|
+
process.exit(0);
|
|
198
|
+
}
|
|
199
|
+
console.log('✅ A.L.I.C.E. is healthy\n');
|
|
200
|
+
process.exit(0);
|
|
201
|
+
}
|
|
202
|
+
|
|
130
203
|
printBanner();
|
|
131
204
|
|
|
132
205
|
// 1. Detect OpenClaw — offer to install if missing
|
|
@@ -139,6 +212,10 @@ export async function runInstall(options = {}) {
|
|
|
139
212
|
console.error(' ❌ OpenClaw config not found. Run: openclaw configure\n');
|
|
140
213
|
process.exit(1);
|
|
141
214
|
}
|
|
215
|
+
|
|
216
|
+
// 1b. Check for OpenClaw updates
|
|
217
|
+
await checkForOpenClawUpdate(auto);
|
|
218
|
+
|
|
142
219
|
console.log(' ✓ OpenClaw detected\n');
|
|
143
220
|
|
|
144
221
|
const allAgents = loadAgentRegistry();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robbiesrobotics/alice-agents",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
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"
|
|
@@ -20,7 +20,10 @@
|
|
|
20
20
|
"files": [
|
|
21
21
|
"bin/",
|
|
22
22
|
"lib/",
|
|
23
|
+
"tools/",
|
|
24
|
+
"snapshots/",
|
|
23
25
|
"templates/",
|
|
26
|
+
"SELF-HEALING-SPEC.md",
|
|
24
27
|
"README.md"
|
|
25
28
|
],
|
|
26
29
|
"publishConfig": {
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"capturedAt": "2026-03-16T00:00:00Z",
|
|
4
|
+
"openclawVersion": "2026.3.13",
|
|
5
|
+
"aliceVersion": "1.1.1",
|
|
6
|
+
"description": "Config fields A.L.I.C.E. depends on",
|
|
7
|
+
"checksum": "458186002246ca46"
|
|
8
|
+
},
|
|
9
|
+
"agents": {
|
|
10
|
+
"defaults": {
|
|
11
|
+
"model": {
|
|
12
|
+
"path": "agents.defaults.model",
|
|
13
|
+
"required": true,
|
|
14
|
+
"fields": {
|
|
15
|
+
"primary": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"example": "anthropic/claude-sonnet-4-6"
|
|
18
|
+
},
|
|
19
|
+
"fallbacks": {
|
|
20
|
+
"type": "array"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"workspace": {
|
|
25
|
+
"path": "agents.defaults.workspace",
|
|
26
|
+
"type": "string",
|
|
27
|
+
"required": true
|
|
28
|
+
},
|
|
29
|
+
"heartbeat": {
|
|
30
|
+
"path": "agents.defaults.heartbeat",
|
|
31
|
+
"required": false,
|
|
32
|
+
"fields": {
|
|
33
|
+
"every": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"example": "15m"
|
|
36
|
+
},
|
|
37
|
+
"target": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"allowedValues": [
|
|
40
|
+
"last",
|
|
41
|
+
"new"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"directPolicy": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"allowedValues": [
|
|
47
|
+
"allow",
|
|
48
|
+
"deny"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"prompt": {
|
|
52
|
+
"type": "string"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"maxConcurrent": {
|
|
57
|
+
"path": "agents.defaults.maxConcurrent",
|
|
58
|
+
"type": "number",
|
|
59
|
+
"required": false
|
|
60
|
+
},
|
|
61
|
+
"subagents": {
|
|
62
|
+
"path": "agents.defaults.subagents",
|
|
63
|
+
"fields": {
|
|
64
|
+
"maxConcurrent": {
|
|
65
|
+
"type": "number"
|
|
66
|
+
},
|
|
67
|
+
"archiveAfterMinutes": {
|
|
68
|
+
"type": "number"
|
|
69
|
+
},
|
|
70
|
+
"runTimeoutSeconds": {
|
|
71
|
+
"type": "number"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"compaction": {
|
|
76
|
+
"path": "agents.defaults.compaction",
|
|
77
|
+
"fields": {
|
|
78
|
+
"mode": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"allowedValues": [
|
|
81
|
+
"safeguard",
|
|
82
|
+
"auto",
|
|
83
|
+
"off"
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"list_entry": {
|
|
90
|
+
"description": "Schema for each entry in agents.list",
|
|
91
|
+
"fields": {
|
|
92
|
+
"id": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"required": true
|
|
95
|
+
},
|
|
96
|
+
"name": {
|
|
97
|
+
"type": "string",
|
|
98
|
+
"required": true
|
|
99
|
+
},
|
|
100
|
+
"workspace": {
|
|
101
|
+
"type": "string",
|
|
102
|
+
"required": true
|
|
103
|
+
},
|
|
104
|
+
"identity": {
|
|
105
|
+
"required": true,
|
|
106
|
+
"fields": {
|
|
107
|
+
"name": {
|
|
108
|
+
"type": "string"
|
|
109
|
+
},
|
|
110
|
+
"theme": {
|
|
111
|
+
"type": "string"
|
|
112
|
+
},
|
|
113
|
+
"emoji": {
|
|
114
|
+
"type": "string"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"sandbox": {
|
|
119
|
+
"required": true,
|
|
120
|
+
"fields": {
|
|
121
|
+
"mode": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"allowedValues": [
|
|
124
|
+
"off",
|
|
125
|
+
"all"
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
"scope": {
|
|
129
|
+
"type": "string",
|
|
130
|
+
"allowedValues": [
|
|
131
|
+
"agent",
|
|
132
|
+
"workspace"
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"tools": {
|
|
138
|
+
"required": true,
|
|
139
|
+
"variants": {
|
|
140
|
+
"profile_based": {
|
|
141
|
+
"fields": {
|
|
142
|
+
"profile": {
|
|
143
|
+
"type": "string",
|
|
144
|
+
"allowedValues": [
|
|
145
|
+
"full",
|
|
146
|
+
"coding",
|
|
147
|
+
"basic"
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
"alsoAllow": {
|
|
151
|
+
"type": "array"
|
|
152
|
+
},
|
|
153
|
+
"deny": {
|
|
154
|
+
"type": "array"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"explicit": {
|
|
159
|
+
"fields": {
|
|
160
|
+
"allow": {
|
|
161
|
+
"type": "array",
|
|
162
|
+
"required": true
|
|
163
|
+
},
|
|
164
|
+
"deny": {
|
|
165
|
+
"type": "array"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
"model": {
|
|
172
|
+
"type": "string",
|
|
173
|
+
"required": false
|
|
174
|
+
},
|
|
175
|
+
"default": {
|
|
176
|
+
"type": "boolean",
|
|
177
|
+
"required": false
|
|
178
|
+
},
|
|
179
|
+
"subagents": {
|
|
180
|
+
"type": "object",
|
|
181
|
+
"required": false
|
|
182
|
+
},
|
|
183
|
+
"groupChat": {
|
|
184
|
+
"type": "object",
|
|
185
|
+
"required": false
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
"tools": {
|
|
191
|
+
"agentToAgent": {
|
|
192
|
+
"path": "tools.agentToAgent",
|
|
193
|
+
"fields": {
|
|
194
|
+
"allow": {
|
|
195
|
+
"type": "array",
|
|
196
|
+
"description": "Agent IDs that can receive spawned tasks"
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"hooks": {
|
|
202
|
+
"knownHooks": [
|
|
203
|
+
"boot-md",
|
|
204
|
+
"bootstrap-extra-files",
|
|
205
|
+
"command-logger",
|
|
206
|
+
"session-memory"
|
|
207
|
+
],
|
|
208
|
+
"path": "hooks.entries"
|
|
209
|
+
},
|
|
210
|
+
"sessions": {
|
|
211
|
+
"path": "sessions",
|
|
212
|
+
"fields": {
|
|
213
|
+
"plugins": {
|
|
214
|
+
"type": "object"
|
|
215
|
+
},
|
|
216
|
+
"capabilities": {
|
|
217
|
+
"type": "object"
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"capturedAt": "2026-03-16T00:00:00Z",
|
|
4
|
+
"openclawVersion": "2026.3.13",
|
|
5
|
+
"aliceVersion": "1.1.1",
|
|
6
|
+
"description": "Tools A.L.I.C.E. references in agent configs"
|
|
7
|
+
},
|
|
8
|
+
"tools": {
|
|
9
|
+
"read": { "criticality": "core", "usedBy": ["all"] },
|
|
10
|
+
"write": { "criticality": "core", "usedBy": ["all"] },
|
|
11
|
+
"edit": { "criticality": "core", "usedBy": ["dylan","darius","avery","isaac","felix","caleb","devon","quinn","alex","daphne"] },
|
|
12
|
+
"exec": { "criticality": "core", "usedBy": ["dylan","darius","avery","isaac","owen","selena","devon","quinn","alex","felix","caleb"] },
|
|
13
|
+
"process": { "criticality": "core", "usedBy": ["dylan","darius","avery","isaac","devon","quinn"] },
|
|
14
|
+
"apply_patch": { "criticality": "core", "usedBy": ["dylan","darius","avery","isaac","felix","caleb","devon","quinn","alex","daphne"] },
|
|
15
|
+
"web_search": { "criticality": "core", "usedBy": ["all"] },
|
|
16
|
+
"web_fetch": { "criticality": "core", "usedBy": ["all"] },
|
|
17
|
+
"browser": { "criticality": "optional", "usedBy": ["olivia","rowan","quinn","uma","alex","tommy","nadia","morgan","sloane"] },
|
|
18
|
+
"canvas": { "criticality": "optional", "usedBy": ["olivia","nadia"] },
|
|
19
|
+
"message": { "criticality": "optional", "usedBy": ["olivia","clara","sophie","sloane","morgan","eva","parker"] },
|
|
20
|
+
"tts": { "criticality": "optional", "usedBy": ["olivia"] },
|
|
21
|
+
"sessions_spawn": { "criticality": "core", "usedBy": ["olivia"] },
|
|
22
|
+
"sessions_send": { "criticality": "core", "usedBy": ["olivia"] },
|
|
23
|
+
"sessions_list": { "criticality": "core", "usedBy": ["olivia","all"] },
|
|
24
|
+
"sessions_history":{ "criticality": "core", "usedBy": ["olivia","all"] },
|
|
25
|
+
"session_status": { "criticality": "core", "usedBy": ["olivia","all"] },
|
|
26
|
+
"agents_list": { "criticality": "core", "usedBy": ["olivia"] },
|
|
27
|
+
"cron": { "criticality": "optional", "usedBy": ["olivia","avery","tommy","sloane","morgan","eva","parker"] },
|
|
28
|
+
"memory_search": { "criticality": "core", "usedBy": ["olivia"] },
|
|
29
|
+
"memory_get": { "criticality": "core", "usedBy": ["olivia"] },
|
|
30
|
+
"image": { "criticality": "optional", "usedBy": ["olivia"] },
|
|
31
|
+
"pdf": { "criticality": "optional", "usedBy": ["olivia"] }
|
|
32
|
+
},
|
|
33
|
+
"profiles": {
|
|
34
|
+
"full": { "description": "All tools available", "usedBy": ["olivia"] },
|
|
35
|
+
"coding": { "description": "Code-focused toolset with exec/edit/apply_patch", "usedBy": ["dylan","darius","avery","isaac","devon","felix","quinn","alex","caleb"] }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* A.L.I.C.E. Compatibility Checker
|
|
4
|
+
* Diffs current OpenClaw installation against stored snapshots.
|
|
5
|
+
* Exit 0 = compatible, 1 = breaking changes found.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync, existsSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { join, dirname } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { execSync } from 'node:child_process';
|
|
12
|
+
import { createHash } from 'node:crypto';
|
|
13
|
+
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const ROOT = join(__dirname, '..');
|
|
16
|
+
|
|
17
|
+
// --- CLI args ---
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
const verbose = args.includes('--verbose');
|
|
20
|
+
const outputFile = args.find(a => a.startsWith('--output='))?.split('=')[1];
|
|
21
|
+
|
|
22
|
+
function log(...a) { if (verbose) console.log(...a); }
|
|
23
|
+
function warn(msg) { console.warn(` ⚠️ ${msg}`); }
|
|
24
|
+
|
|
25
|
+
// --- Helpers ---
|
|
26
|
+
function loadJSON(path) {
|
|
27
|
+
if (!existsSync(path)) return null;
|
|
28
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getOpenClawVersion() {
|
|
32
|
+
try {
|
|
33
|
+
const out = execSync('openclaw --version 2>/dev/null', { encoding: 'utf8', stdio: ['pipe','pipe','pipe'] });
|
|
34
|
+
const m = out.match(/(\d{4}\.\d+\.\d+)/);
|
|
35
|
+
return m ? m[1] : 'unknown';
|
|
36
|
+
} catch { return 'unknown'; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getOpenClawConfig() {
|
|
40
|
+
const configPath = join(process.env.HOME, '.openclaw', 'openclaw.json');
|
|
41
|
+
return loadJSON(configPath);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// --- Diff functions ---
|
|
45
|
+
|
|
46
|
+
function diffConfigSchema(snapshot, liveConfig) {
|
|
47
|
+
const changes = [];
|
|
48
|
+
|
|
49
|
+
// Check agents.defaults fields
|
|
50
|
+
const defaults = liveConfig?.agents?.defaults || {};
|
|
51
|
+
const snap = snapshot?.agents?.defaults || {};
|
|
52
|
+
|
|
53
|
+
if (!defaults.model?.primary) {
|
|
54
|
+
changes.push({
|
|
55
|
+
category: 'config',
|
|
56
|
+
severity: 'high',
|
|
57
|
+
field: 'agents.defaults.model.primary',
|
|
58
|
+
change: 'Field missing or undefined',
|
|
59
|
+
autoFixable: false
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check sandbox allowedValues
|
|
64
|
+
const agents = liveConfig?.agents?.list || [];
|
|
65
|
+
for (const agent of agents) {
|
|
66
|
+
const mode = agent?.sandbox?.mode;
|
|
67
|
+
const allowed = ['off', 'all'];
|
|
68
|
+
if (mode && !allowed.includes(mode)) {
|
|
69
|
+
changes.push({
|
|
70
|
+
category: 'config',
|
|
71
|
+
severity: 'high',
|
|
72
|
+
field: `agents.list[${agent.id}].sandbox.mode`,
|
|
73
|
+
change: `Unexpected value '${mode}', expected one of: ${allowed.join(', ')}`,
|
|
74
|
+
autoFixable: false
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Check compaction mode
|
|
80
|
+
const compactionMode = defaults?.compaction?.mode;
|
|
81
|
+
const validModes = ['safeguard', 'auto', 'off'];
|
|
82
|
+
if (compactionMode && !validModes.includes(compactionMode)) {
|
|
83
|
+
changes.push({
|
|
84
|
+
category: 'behavioral',
|
|
85
|
+
severity: 'medium',
|
|
86
|
+
field: 'agents.defaults.compaction.mode',
|
|
87
|
+
change: `Value '${compactionMode}' may not be valid. Expected: ${validModes.join(', ')}`,
|
|
88
|
+
autoFixable: false
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return changes;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function diffToolAPI(snapshot, liveConfig) {
|
|
96
|
+
const changes = [];
|
|
97
|
+
const snapTools = snapshot?.tools || {};
|
|
98
|
+
|
|
99
|
+
// Get all tools referenced in agent configs
|
|
100
|
+
const referencedTools = new Set();
|
|
101
|
+
const referencedProfiles = new Set();
|
|
102
|
+
|
|
103
|
+
for (const agent of liveConfig?.agents?.list || []) {
|
|
104
|
+
const t = agent?.tools || {};
|
|
105
|
+
if (t.profile) referencedProfiles.add(t.profile);
|
|
106
|
+
for (const tool of [...(t.allow || []), ...(t.alsoAllow || []), ...(t.deny || [])]) {
|
|
107
|
+
referencedTools.add(tool);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const knownTools = new Set(Object.keys(snapTools));
|
|
112
|
+
|
|
113
|
+
// Check for tools in use that aren't in our snapshot (may be new — not breaking)
|
|
114
|
+
for (const tool of referencedTools) {
|
|
115
|
+
if (!knownTools.has(tool)) {
|
|
116
|
+
log(` Note: Tool '${tool}' not in snapshot — may be new`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check snapshot profiles still exist
|
|
121
|
+
const snapProfiles = snapshot?.profiles || {};
|
|
122
|
+
for (const profile of referencedProfiles) {
|
|
123
|
+
if (!snapProfiles[profile]) {
|
|
124
|
+
// Can't verify, just note
|
|
125
|
+
log(` Note: Profile '${profile}' used but not in snapshot`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return changes;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function diffBehavioral(snapshot, liveConfig) {
|
|
133
|
+
const changes = [];
|
|
134
|
+
const defaults = liveConfig?.agents?.defaults || {};
|
|
135
|
+
const snapDefaults = snapshot?.agents?.defaults || {};
|
|
136
|
+
|
|
137
|
+
// Check heartbeat still works
|
|
138
|
+
if (defaults.heartbeat && !defaults.heartbeat.every) {
|
|
139
|
+
changes.push({
|
|
140
|
+
category: 'behavioral',
|
|
141
|
+
severity: 'medium',
|
|
142
|
+
field: 'agents.defaults.heartbeat.every',
|
|
143
|
+
change: 'Heartbeat interval missing',
|
|
144
|
+
autoFixable: true,
|
|
145
|
+
fix: { type: 'field-add', path: 'agents.defaults.heartbeat.every', value: '15m' }
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check subagents runTimeoutSeconds still present
|
|
150
|
+
if (defaults.subagents && !defaults.subagents.runTimeoutSeconds) {
|
|
151
|
+
changes.push({
|
|
152
|
+
category: 'behavioral',
|
|
153
|
+
severity: 'low',
|
|
154
|
+
field: 'agents.defaults.subagents.runTimeoutSeconds',
|
|
155
|
+
change: 'runTimeoutSeconds missing from subagents defaults',
|
|
156
|
+
autoFixable: true,
|
|
157
|
+
fix: { type: 'field-add', path: 'agents.defaults.subagents.runTimeoutSeconds', value: 900 }
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return changes;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function diffSkills() {
|
|
165
|
+
const changes = [];
|
|
166
|
+
// Check if skills directory structure is intact
|
|
167
|
+
const skillsPath = join(
|
|
168
|
+
process.env.HOME,
|
|
169
|
+
'.local/share/fnm/node-versions',
|
|
170
|
+
);
|
|
171
|
+
// Skills live in node_modules/openclaw/skills — check a known skill exists
|
|
172
|
+
try {
|
|
173
|
+
execSync('ls ~/.local/share/fnm/node-versions/*/installation/lib/node_modules/openclaw/skills/coding-agent/SKILL.md 2>/dev/null | head -1', {
|
|
174
|
+
encoding: 'utf8', shell: '/bin/sh', stdio: ['pipe','pipe','pipe']
|
|
175
|
+
});
|
|
176
|
+
} catch {
|
|
177
|
+
changes.push({
|
|
178
|
+
category: 'skills',
|
|
179
|
+
severity: 'medium',
|
|
180
|
+
field: 'skills.coding-agent',
|
|
181
|
+
change: 'coding-agent skill not found in expected location',
|
|
182
|
+
autoFixable: false
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return changes;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// --- Main ---
|
|
189
|
+
async function main() {
|
|
190
|
+
const openclawVersion = getOpenClawVersion();
|
|
191
|
+
const aliceVersion = loadJSON(join(ROOT, 'package.json'))?.version || 'unknown';
|
|
192
|
+
|
|
193
|
+
console.log(`\n 🔍 A.L.I.C.E. Compatibility Checker`);
|
|
194
|
+
console.log(` OpenClaw: ${openclawVersion} | A.L.I.C.E.: ${aliceVersion}\n`);
|
|
195
|
+
|
|
196
|
+
const schemaSnapshot = loadJSON(join(ROOT, 'snapshots', 'schema-snapshot.json'));
|
|
197
|
+
const toolSnapshot = loadJSON(join(ROOT, 'snapshots', 'tool-snapshot.json'));
|
|
198
|
+
const liveConfig = getOpenClawConfig();
|
|
199
|
+
|
|
200
|
+
if (!schemaSnapshot || !toolSnapshot) {
|
|
201
|
+
warn('Snapshots missing — cannot check compatibility');
|
|
202
|
+
process.exit(0);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!liveConfig) {
|
|
206
|
+
warn('OpenClaw config not found — is OpenClaw installed and configured?');
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const breakingChanges = [
|
|
211
|
+
...diffConfigSchema(schemaSnapshot, liveConfig),
|
|
212
|
+
...diffToolAPI(toolSnapshot, liveConfig),
|
|
213
|
+
...diffBehavioral(schemaSnapshot, liveConfig),
|
|
214
|
+
...diffSkills()
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
const criticals = breakingChanges.filter(c => c.severity === 'critical' || c.severity === 'high');
|
|
218
|
+
const mediums = breakingChanges.filter(c => c.severity === 'medium');
|
|
219
|
+
const lows = breakingChanges.filter(c => c.severity === 'low');
|
|
220
|
+
const autoFixable = breakingChanges.filter(c => c.autoFixable);
|
|
221
|
+
|
|
222
|
+
const report = {
|
|
223
|
+
generatedAt: new Date().toISOString(),
|
|
224
|
+
openclawVersion,
|
|
225
|
+
aliceVersion,
|
|
226
|
+
snapshotVersion: schemaSnapshot.meta?.openclawVersion,
|
|
227
|
+
compatible: breakingChanges.length === 0,
|
|
228
|
+
summary: {
|
|
229
|
+
total: breakingChanges.length,
|
|
230
|
+
critical: criticals.length,
|
|
231
|
+
medium: mediums.length,
|
|
232
|
+
low: lows.length,
|
|
233
|
+
autoFixable: autoFixable.length
|
|
234
|
+
},
|
|
235
|
+
breakingChanges
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
if (outputFile) {
|
|
239
|
+
writeFileSync(outputFile, JSON.stringify(report, null, 2));
|
|
240
|
+
console.log(` Report written to: ${outputFile}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (breakingChanges.length === 0) {
|
|
244
|
+
console.log(' ✅ Compatible — no breaking changes detected\n');
|
|
245
|
+
process.exit(0);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
console.log(` Found ${breakingChanges.length} issue(s):\n`);
|
|
249
|
+
for (const c of breakingChanges) {
|
|
250
|
+
const icon = c.severity === 'high' || c.severity === 'critical' ? '🔴' :
|
|
251
|
+
c.severity === 'medium' ? '🟡' : '🟢';
|
|
252
|
+
const fix = c.autoFixable ? ' [auto-fixable]' : ' [manual review]';
|
|
253
|
+
console.log(` ${icon} [${c.category}] ${c.field}`);
|
|
254
|
+
console.log(` ${c.change}${fix}\n`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (autoFixable.length > 0) {
|
|
258
|
+
console.log(` 💡 ${autoFixable.length} issue(s) can be auto-fixed.`);
|
|
259
|
+
console.log(` Run: npx @robbiesrobotics/alice-agents --health\n`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
process.exit(criticals.length > 0 ? 1 : 0);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
main().catch(err => {
|
|
266
|
+
console.error('Checker error:', err.message);
|
|
267
|
+
process.exit(1);
|
|
268
|
+
});
|