@kognai/orchestrator-core 0.1.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/README.md +44 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +175 -0
- package/dist/lib/aar-middleware.d.ts +6 -0
- package/dist/lib/aar-middleware.js +70 -0
- package/dist/lib/aar-types.d.ts +34 -0
- package/dist/lib/aar-types.js +4 -0
- package/dist/lib/acp-engine.d.ts +68 -0
- package/dist/lib/acp-engine.js +123 -0
- package/dist/lib/acp.d.ts +61 -0
- package/dist/lib/acp.js +425 -0
- package/dist/lib/agent-registry.d.ts +50 -0
- package/dist/lib/agent-registry.js +137 -0
- package/dist/lib/anthropic-direct.d.ts +27 -0
- package/dist/lib/anthropic-direct.js +109 -0
- package/dist/lib/asmr-extractor.d.ts +40 -0
- package/dist/lib/asmr-extractor.js +151 -0
- package/dist/lib/asmr-retrieval.d.ts +76 -0
- package/dist/lib/asmr-retrieval.js +311 -0
- package/dist/lib/asmr.d.ts +8 -0
- package/dist/lib/asmr.js +24 -0
- package/dist/lib/brainx-client.d.ts +72 -0
- package/dist/lib/brainx-client.js +200 -0
- package/dist/lib/brainx-embed.d.ts +14 -0
- package/dist/lib/brainx-embed.js +139 -0
- package/dist/lib/brainx-swarm-bridge.d.ts +93 -0
- package/dist/lib/brainx-swarm-bridge.js +242 -0
- package/dist/lib/byterover-client.d.ts +19 -0
- package/dist/lib/byterover-client.js +59 -0
- package/dist/lib/ceo-wallet.d.ts +37 -0
- package/dist/lib/ceo-wallet.js +176 -0
- package/dist/lib/chomsky-gate.d.ts +24 -0
- package/dist/lib/chomsky-gate.js +178 -0
- package/dist/lib/chomsky-runner.d.ts +29 -0
- package/dist/lib/chomsky-runner.js +157 -0
- package/dist/lib/citizen-score-contract.d.ts +72 -0
- package/dist/lib/citizen-score-contract.js +16 -0
- package/dist/lib/citizen-score-registry.d.ts +25 -0
- package/dist/lib/citizen-score-registry.js +65 -0
- package/dist/lib/citizenship.d.ts +103 -0
- package/dist/lib/citizenship.js +272 -0
- package/dist/lib/clawrouter-client.d.ts +37 -0
- package/dist/lib/clawrouter-client.js +148 -0
- package/dist/lib/code-asset-crystalliser.d.ts +41 -0
- package/dist/lib/code-asset-crystalliser.js +181 -0
- package/dist/lib/code-failure-logger.d.ts +27 -0
- package/dist/lib/code-failure-logger.js +42 -0
- package/dist/lib/cto-approval-gate.d.ts +45 -0
- package/dist/lib/cto-approval-gate.js +478 -0
- package/dist/lib/cto-gate-types.d.ts +28 -0
- package/dist/lib/cto-gate-types.js +8 -0
- package/dist/lib/decomposer-feedback.d.ts +54 -0
- package/dist/lib/decomposer-feedback.js +115 -0
- package/dist/lib/emotional-safety-gate.d.ts +48 -0
- package/dist/lib/emotional-safety-gate.js +97 -0
- package/dist/lib/engine-paths.d.ts +13 -0
- package/dist/lib/engine-paths.js +32 -0
- package/dist/lib/event-bus-listener.d.ts +8 -0
- package/dist/lib/event-bus-listener.js +144 -0
- package/dist/lib/event-bus-publisher.d.ts +25 -0
- package/dist/lib/event-bus-publisher.js +188 -0
- package/dist/lib/event-bus-types.d.ts +73 -0
- package/dist/lib/event-bus-types.js +23 -0
- package/dist/lib/failure-library.d.ts +178 -0
- package/dist/lib/failure-library.js +349 -0
- package/dist/lib/ksl/error-log.d.ts +28 -0
- package/dist/lib/ksl/error-log.js +43 -0
- package/dist/lib/ksl/index.d.ts +9 -0
- package/dist/lib/ksl/index.js +25 -0
- package/dist/lib/ksl/orchestrator-tap.d.ts +16 -0
- package/dist/lib/ksl/orchestrator-tap.js +85 -0
- package/dist/lib/ksl/record-writer.d.ts +46 -0
- package/dist/lib/ksl/record-writer.js +45 -0
- package/dist/lib/llm-cost-table.d.ts +36 -0
- package/dist/lib/llm-cost-table.js +90 -0
- package/dist/lib/local-model-router.d.ts +27 -0
- package/dist/lib/local-model-router.js +61 -0
- package/dist/lib/mc-client.d.ts +51 -0
- package/dist/lib/mc-client.js +249 -0
- package/dist/lib/model-router-contract.d.ts +91 -0
- package/dist/lib/model-router-contract.js +19 -0
- package/dist/lib/model-router-registry.d.ts +24 -0
- package/dist/lib/model-router-registry.js +52 -0
- package/dist/lib/model-router.d.ts +20 -0
- package/dist/lib/model-router.js +79 -0
- package/dist/lib/monotask-state-machine.d.ts +19 -0
- package/dist/lib/monotask-state-machine.js +131 -0
- package/dist/lib/neutral-prompt-checker.d.ts +22 -0
- package/dist/lib/neutral-prompt-checker.js +130 -0
- package/dist/lib/notion-direct.d.ts +92 -0
- package/dist/lib/notion-direct.js +381 -0
- package/dist/lib/ollama-client.d.ts +37 -0
- package/dist/lib/ollama-client.js +158 -0
- package/dist/lib/omel/credential-vault.d.ts +57 -0
- package/dist/lib/omel/credential-vault.js +324 -0
- package/dist/lib/omel/human-brake.d.ts +32 -0
- package/dist/lib/omel/human-brake.js +289 -0
- package/dist/lib/omel/index.d.ts +10 -0
- package/dist/lib/omel/index.js +26 -0
- package/dist/lib/omel/phantom-workspace.d.ts +31 -0
- package/dist/lib/omel/phantom-workspace.js +256 -0
- package/dist/lib/omel/wipe-witness.d.ts +75 -0
- package/dist/lib/omel/wipe-witness.js +398 -0
- package/dist/lib/orchestrate-engine.d.ts +25 -0
- package/dist/lib/orchestrate-engine.js +4436 -0
- package/dist/lib/perm-judge.d.ts +46 -0
- package/dist/lib/perm-judge.js +173 -0
- package/dist/lib/plumber/conformance.d.ts +54 -0
- package/dist/lib/plumber/conformance.js +121 -0
- package/dist/lib/plumber/index.d.ts +9 -0
- package/dist/lib/plumber/index.js +25 -0
- package/dist/lib/plumber/observer.d.ts +52 -0
- package/dist/lib/plumber/observer.js +180 -0
- package/dist/lib/plumber/types.d.ts +78 -0
- package/dist/lib/plumber/types.js +29 -0
- package/dist/lib/research-impl-gate.d.ts +16 -0
- package/dist/lib/research-impl-gate.js +105 -0
- package/dist/lib/sherlock-memory.d.ts +29 -0
- package/dist/lib/sherlock-memory.js +105 -0
- package/dist/lib/skill-crystalliser.d.ts +44 -0
- package/dist/lib/skill-crystalliser.js +60 -0
- package/dist/lib/sprint-runner-engine.d.ts +27 -0
- package/dist/lib/sprint-runner-engine.js +1042 -0
- package/dist/lib/sprint-state.d.ts +71 -0
- package/dist/lib/sprint-state.js +202 -0
- package/dist/lib/stuck-handler.d.ts +17 -0
- package/dist/lib/stuck-handler.js +249 -0
- package/dist/lib/task-contract-checker.d.ts +17 -0
- package/dist/lib/task-contract-checker.js +29 -0
- package/dist/lib/task-router/index.d.ts +17 -0
- package/dist/lib/task-router/index.js +52 -0
- package/dist/lib/task-router/router/generate-execution-id.d.ts +10 -0
- package/dist/lib/task-router/router/generate-execution-id.js +24 -0
- package/dist/lib/task-router/router/resolve-route.d.ts +2 -0
- package/dist/lib/task-router/router/resolve-route.js +49 -0
- package/dist/lib/task-router/types.d.ts +79 -0
- package/dist/lib/task-router/types.js +39 -0
- package/dist/lib/token-budget-validator.d.ts +44 -0
- package/dist/lib/token-budget-validator.js +84 -0
- package/dist/lib/trust-score-updater.d.ts +30 -0
- package/dist/lib/trust-score-updater.js +107 -0
- package/dist/lib/wallet-state.d.ts +26 -0
- package/dist/lib/wallet-state.js +85 -0
- package/package.json +27 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// OMEL Wipe Witness — Sprint 178 + Sprint 183 Hardening (AMD-13)
|
|
3
|
+
// Detection layer for destructive agent writes and file deletions.
|
|
4
|
+
// Would have caught Sprint 171 qwen3:14b 2800-line file rewrites.
|
|
5
|
+
//
|
|
6
|
+
// Public API:
|
|
7
|
+
// beforeWrite(filePath, agentId) → WitnessToken (SHA-256 + size before write)
|
|
8
|
+
// afterWrite(token, newSizeBytes) → void (shrink alert if new < 50% old)
|
|
9
|
+
// beforeDelete(filePath, agentId) → void (log deletion event)
|
|
10
|
+
// getShrinkAlerts() → ShrinkAlert[]
|
|
11
|
+
// rollback(filePath, stepsBack?) → boolean (Sprint 183)
|
|
12
|
+
// integrityReport(scanDir) → IntegrityReport (Sprint 183)
|
|
13
|
+
//
|
|
14
|
+
// Sprint 183 hardening additions:
|
|
15
|
+
// - Last-3-state rollback: restore file to any of last 3 pre-write states
|
|
16
|
+
// - Auto-rollback on ShrinkAlert unless WIPE_WITNESS_NO_ROLLBACK=true
|
|
17
|
+
// - Nightly integrity report: scan agent files vs last known hashes
|
|
18
|
+
// - run-swarm.sh integration: pre-run snapshot + post-run drift report
|
|
19
|
+
//
|
|
20
|
+
// Audit log: logs/omel/wipe-witness-YYYY-MM-DD.jsonl
|
|
21
|
+
// Telegram: TELEGRAM_BOT_TOKEN / OWNER_TELEGRAM_CHAT_ID (fire-and-forget)
|
|
22
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
25
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
26
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
27
|
+
}
|
|
28
|
+
Object.defineProperty(o, k2, desc);
|
|
29
|
+
}) : (function(o, m, k, k2) {
|
|
30
|
+
if (k2 === undefined) k2 = k;
|
|
31
|
+
o[k2] = m[k];
|
|
32
|
+
}));
|
|
33
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
34
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
35
|
+
}) : function(o, v) {
|
|
36
|
+
o["default"] = v;
|
|
37
|
+
});
|
|
38
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
39
|
+
var ownKeys = function(o) {
|
|
40
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
41
|
+
var ar = [];
|
|
42
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
43
|
+
return ar;
|
|
44
|
+
};
|
|
45
|
+
return ownKeys(o);
|
|
46
|
+
};
|
|
47
|
+
return function (mod) {
|
|
48
|
+
if (mod && mod.__esModule) return mod;
|
|
49
|
+
var result = {};
|
|
50
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
51
|
+
__setModuleDefault(result, mod);
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
})();
|
|
55
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.wipeWitness = exports.WipeWitness = void 0;
|
|
57
|
+
const fs = __importStar(require("fs"));
|
|
58
|
+
const path = __importStar(require("path"));
|
|
59
|
+
const engine_paths_1 = require("../engine-paths");
|
|
60
|
+
const https = __importStar(require("https"));
|
|
61
|
+
const crypto_1 = require("crypto");
|
|
62
|
+
const LOGS_DIR = path.join((0, engine_paths_1.resolveEnginePaths)().root, 'logs', 'omel');
|
|
63
|
+
const SNAPSHOTS_DIR = path.join(LOGS_DIR, 'wipe-witness-snapshots');
|
|
64
|
+
const HASHES_FILE = path.join(LOGS_DIR, 'wipe-witness-hashes.json');
|
|
65
|
+
fs.mkdirSync(LOGS_DIR, { recursive: true });
|
|
66
|
+
fs.mkdirSync(SNAPSHOTS_DIR, { recursive: true });
|
|
67
|
+
const LOG_FILE = () => {
|
|
68
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
69
|
+
return path.join(LOGS_DIR, `wipe-witness-${date}.jsonl`);
|
|
70
|
+
};
|
|
71
|
+
// ── Internal helpers ──────────────────────────────────────────────────────────
|
|
72
|
+
function appendLog(entry) {
|
|
73
|
+
try {
|
|
74
|
+
fs.appendFileSync(LOG_FILE(), JSON.stringify(entry) + '\n');
|
|
75
|
+
}
|
|
76
|
+
catch { /* never crash caller */ }
|
|
77
|
+
}
|
|
78
|
+
function sha256File(filePath) {
|
|
79
|
+
try {
|
|
80
|
+
const data = fs.readFileSync(filePath);
|
|
81
|
+
return (0, crypto_1.createHash)('sha256').update(data).digest('hex');
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return '';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function sha256Buf(buf) {
|
|
88
|
+
return (0, crypto_1.createHash)('sha256').update(buf).digest('hex');
|
|
89
|
+
}
|
|
90
|
+
function sendTelegramAlert(message) {
|
|
91
|
+
const botToken = process.env.TELEGRAM_BOT_TOKEN || '';
|
|
92
|
+
const chatId = process.env.OWNER_TELEGRAM_CHAT_ID || '';
|
|
93
|
+
if (!botToken || !chatId)
|
|
94
|
+
return; // silently skip if not configured
|
|
95
|
+
const payload = JSON.stringify({ chat_id: chatId, text: message, parse_mode: 'Markdown' });
|
|
96
|
+
try {
|
|
97
|
+
const req = https.request({
|
|
98
|
+
hostname: 'api.telegram.org',
|
|
99
|
+
path: `/bot${botToken}/sendMessage`,
|
|
100
|
+
method: 'POST',
|
|
101
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
|
|
102
|
+
});
|
|
103
|
+
req.on('error', () => { }); // fire-and-forget, never crash server
|
|
104
|
+
req.write(payload);
|
|
105
|
+
req.end();
|
|
106
|
+
}
|
|
107
|
+
catch { /* silent */ }
|
|
108
|
+
}
|
|
109
|
+
function loadSnapshotStore() {
|
|
110
|
+
const storePath = path.join(SNAPSHOTS_DIR, 'index.json');
|
|
111
|
+
try {
|
|
112
|
+
if (fs.existsSync(storePath)) {
|
|
113
|
+
return JSON.parse(fs.readFileSync(storePath, 'utf-8'));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch { /* ignore */ }
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
function saveSnapshotStore(store) {
|
|
120
|
+
const storePath = path.join(SNAPSHOTS_DIR, 'index.json');
|
|
121
|
+
try {
|
|
122
|
+
fs.writeFileSync(storePath, JSON.stringify(store, null, 2), 'utf-8');
|
|
123
|
+
}
|
|
124
|
+
catch { /* non-fatal */ }
|
|
125
|
+
}
|
|
126
|
+
function snapshotFile(filePath) {
|
|
127
|
+
try {
|
|
128
|
+
if (!fs.existsSync(filePath))
|
|
129
|
+
return null;
|
|
130
|
+
const data = fs.readFileSync(filePath);
|
|
131
|
+
const hash = sha256Buf(data);
|
|
132
|
+
const key = Buffer.from(filePath).toString('base64').replace(/[/+=]/g, '_');
|
|
133
|
+
const ts = Date.now();
|
|
134
|
+
const snapName = `${key}-${ts}.snap`;
|
|
135
|
+
const snapPath = path.join(SNAPSHOTS_DIR, snapName);
|
|
136
|
+
fs.writeFileSync(snapPath, data);
|
|
137
|
+
return {
|
|
138
|
+
snapshotPath: snapPath,
|
|
139
|
+
hash,
|
|
140
|
+
sizeBytes: data.length,
|
|
141
|
+
savedAt: new Date(ts).toISOString(),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function saveSnapshot(filePath) {
|
|
149
|
+
const store = loadSnapshotStore();
|
|
150
|
+
const snap = snapshotFile(filePath);
|
|
151
|
+
if (!snap)
|
|
152
|
+
return null;
|
|
153
|
+
if (!store[filePath])
|
|
154
|
+
store[filePath] = [];
|
|
155
|
+
store[filePath].unshift(snap); // newest first
|
|
156
|
+
// Keep only last 3
|
|
157
|
+
const evicted = store[filePath].splice(3);
|
|
158
|
+
for (const old of evicted) {
|
|
159
|
+
try {
|
|
160
|
+
fs.unlinkSync(old.snapshotPath);
|
|
161
|
+
}
|
|
162
|
+
catch { /* ok */ }
|
|
163
|
+
}
|
|
164
|
+
saveSnapshotStore(store);
|
|
165
|
+
return snap.snapshotPath;
|
|
166
|
+
}
|
|
167
|
+
function loadHashStore() {
|
|
168
|
+
try {
|
|
169
|
+
if (fs.existsSync(HASHES_FILE)) {
|
|
170
|
+
return JSON.parse(fs.readFileSync(HASHES_FILE, 'utf-8'));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch { /* ignore */ }
|
|
174
|
+
return {};
|
|
175
|
+
}
|
|
176
|
+
function saveHashStore(store) {
|
|
177
|
+
try {
|
|
178
|
+
fs.writeFileSync(HASHES_FILE, JSON.stringify(store, null, 2), 'utf-8');
|
|
179
|
+
}
|
|
180
|
+
catch { /* non-fatal */ }
|
|
181
|
+
}
|
|
182
|
+
// ── WipeWitness class ─────────────────────────────────────────────────────────
|
|
183
|
+
class WipeWitness {
|
|
184
|
+
shrinkAlerts = [];
|
|
185
|
+
/**
|
|
186
|
+
* Capture SHA-256 hash + size of file before write.
|
|
187
|
+
* Saves a snapshot for rollback (last 3 states).
|
|
188
|
+
* If file does not exist (new file): returns token with oldSizeBytes=0 and oldHash=''.
|
|
189
|
+
*/
|
|
190
|
+
beforeWrite(filePath, agentId) {
|
|
191
|
+
const exists = fs.existsSync(filePath);
|
|
192
|
+
const snapshotKey = exists ? (saveSnapshot(filePath) ?? undefined) : undefined;
|
|
193
|
+
const token = {
|
|
194
|
+
filePath,
|
|
195
|
+
agentId,
|
|
196
|
+
oldHash: exists ? sha256File(filePath) : '',
|
|
197
|
+
oldSizeBytes: exists ? fs.statSync(filePath).size : 0,
|
|
198
|
+
createdAt: new Date().toISOString(),
|
|
199
|
+
snapshotKey,
|
|
200
|
+
};
|
|
201
|
+
appendLog({ event: 'before_write', ...token });
|
|
202
|
+
return token;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Compare new size against old size.
|
|
206
|
+
* If new_size < 0.5 * old_size → emit ShrinkAlert + Telegram notification.
|
|
207
|
+
* Auto-rollback unless WIPE_WITNESS_NO_ROLLBACK=true.
|
|
208
|
+
* Skips shrink check if oldSizeBytes === 0 (new file creation).
|
|
209
|
+
*/
|
|
210
|
+
afterWrite(token, newSizeBytes) {
|
|
211
|
+
appendLog({
|
|
212
|
+
event: 'after_write',
|
|
213
|
+
filePath: token.filePath,
|
|
214
|
+
agentId: token.agentId,
|
|
215
|
+
oldSizeBytes: token.oldSizeBytes,
|
|
216
|
+
newSizeBytes,
|
|
217
|
+
ts: new Date().toISOString(),
|
|
218
|
+
});
|
|
219
|
+
if (token.oldSizeBytes === 0)
|
|
220
|
+
return; // new file — no baseline to compare
|
|
221
|
+
const ratio = newSizeBytes / token.oldSizeBytes;
|
|
222
|
+
if (newSizeBytes < 0.5 * token.oldSizeBytes) {
|
|
223
|
+
let rolledBack = false;
|
|
224
|
+
// Auto-rollback unless disabled
|
|
225
|
+
if (process.env.WIPE_WITNESS_NO_ROLLBACK !== 'true' && token.snapshotKey) {
|
|
226
|
+
try {
|
|
227
|
+
fs.copyFileSync(token.snapshotKey, token.filePath);
|
|
228
|
+
rolledBack = true;
|
|
229
|
+
appendLog({
|
|
230
|
+
event: 'auto_rollback',
|
|
231
|
+
filePath: token.filePath,
|
|
232
|
+
agentId: token.agentId,
|
|
233
|
+
from: token.snapshotKey,
|
|
234
|
+
ts: new Date().toISOString(),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
appendLog({
|
|
239
|
+
event: 'auto_rollback_failed',
|
|
240
|
+
filePath: token.filePath,
|
|
241
|
+
error: err.message,
|
|
242
|
+
ts: new Date().toISOString(),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const alert = {
|
|
247
|
+
filePath: token.filePath,
|
|
248
|
+
agentId: token.agentId,
|
|
249
|
+
oldSizeBytes: token.oldSizeBytes,
|
|
250
|
+
newSizeBytes,
|
|
251
|
+
ratio,
|
|
252
|
+
ts: new Date().toISOString(),
|
|
253
|
+
rolledBack,
|
|
254
|
+
};
|
|
255
|
+
this.shrinkAlerts.push(alert);
|
|
256
|
+
appendLog({ event: 'shrink_alert', ...alert });
|
|
257
|
+
const pct = (ratio * 100).toFixed(1);
|
|
258
|
+
sendTelegramAlert(`⚠️ *[OMEL WipeWitness] Shrink Alert*\n` +
|
|
259
|
+
`File: \`${token.filePath}\`\n` +
|
|
260
|
+
`Agent: ${token.agentId}\n` +
|
|
261
|
+
`${token.oldSizeBytes} bytes → ${newSizeBytes} bytes (${pct}% of original)\n` +
|
|
262
|
+
`Threshold: <50% triggers alert\n` +
|
|
263
|
+
(rolledBack
|
|
264
|
+
? `✅ Auto-rolled back to previous state.`
|
|
265
|
+
: `⚠️ Auto-rollback skipped (WIPE_WITNESS_NO_ROLLBACK=true or no snapshot).`));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Log a deletion event before the file is removed.
|
|
270
|
+
* Captures hash + size for audit trail.
|
|
271
|
+
*/
|
|
272
|
+
beforeDelete(filePath, agentId) {
|
|
273
|
+
const exists = fs.existsSync(filePath);
|
|
274
|
+
appendLog({
|
|
275
|
+
event: 'before_delete',
|
|
276
|
+
filePath,
|
|
277
|
+
agentId,
|
|
278
|
+
sizeBytes: exists ? fs.statSync(filePath).size : 0,
|
|
279
|
+
hash: exists ? sha256File(filePath) : '',
|
|
280
|
+
ts: new Date().toISOString(),
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Return all shrink alerts accumulated in this session.
|
|
285
|
+
*/
|
|
286
|
+
getShrinkAlerts() {
|
|
287
|
+
return [...this.shrinkAlerts];
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Restore a file to a previous state (one of last 3 snapshots).
|
|
291
|
+
* @param filePath Absolute path to the file to restore
|
|
292
|
+
* @param stepsBack 1 = most recent snapshot, 2 = second most recent, 3 = oldest. Default 1.
|
|
293
|
+
* @returns true if rollback succeeded, false otherwise.
|
|
294
|
+
*/
|
|
295
|
+
rollback(filePath, stepsBack = 1) {
|
|
296
|
+
const store = loadSnapshotStore();
|
|
297
|
+
const snaps = store[filePath];
|
|
298
|
+
if (!snaps || snaps.length === 0) {
|
|
299
|
+
appendLog({ event: 'rollback_no_snapshot', filePath, stepsBack, ts: new Date().toISOString() });
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
const idx = Math.min(stepsBack - 1, snaps.length - 1);
|
|
303
|
+
const snap = snaps[idx];
|
|
304
|
+
try {
|
|
305
|
+
fs.copyFileSync(snap.snapshotPath, filePath);
|
|
306
|
+
appendLog({
|
|
307
|
+
event: 'rollback',
|
|
308
|
+
filePath,
|
|
309
|
+
stepsBack,
|
|
310
|
+
snapshotPath: snap.snapshotPath,
|
|
311
|
+
savedAt: snap.savedAt,
|
|
312
|
+
ts: new Date().toISOString(),
|
|
313
|
+
});
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
appendLog({
|
|
318
|
+
event: 'rollback_failed',
|
|
319
|
+
filePath,
|
|
320
|
+
stepsBack,
|
|
321
|
+
error: err.message,
|
|
322
|
+
ts: new Date().toISOString(),
|
|
323
|
+
});
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Take a hash snapshot of all .ts files in a directory.
|
|
329
|
+
* Returns a HashStore map for later comparison.
|
|
330
|
+
* Used by run-swarm.sh pre-run.
|
|
331
|
+
*/
|
|
332
|
+
snapshotDir(scanDir) {
|
|
333
|
+
const store = {};
|
|
334
|
+
function walk(dir) {
|
|
335
|
+
try {
|
|
336
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
337
|
+
const full = path.join(dir, entry.name);
|
|
338
|
+
if (entry.isDirectory() && entry.name !== 'node_modules' && entry.name !== '.git') {
|
|
339
|
+
walk(full);
|
|
340
|
+
}
|
|
341
|
+
else if (entry.isFile() && entry.name.endsWith('.ts')) {
|
|
342
|
+
const rel = path.relative(scanDir, full);
|
|
343
|
+
store[rel] = sha256File(full);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
catch { /* skip unreadable dirs */ }
|
|
348
|
+
}
|
|
349
|
+
walk(scanDir);
|
|
350
|
+
saveHashStore(store);
|
|
351
|
+
appendLog({ event: 'snapshot_taken', scan_dir: scanDir, file_count: Object.keys(store).length, ts: new Date().toISOString() });
|
|
352
|
+
return store;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Nightly integrity report: compare current file hashes vs last snapshot.
|
|
356
|
+
* Returns drifted, new, and deleted files.
|
|
357
|
+
*/
|
|
358
|
+
integrityReport(scanDir) {
|
|
359
|
+
const oldStore = loadHashStore();
|
|
360
|
+
const newStore = this.snapshotDir(scanDir);
|
|
361
|
+
const ts = new Date().toISOString();
|
|
362
|
+
const drifted = [];
|
|
363
|
+
const newFiles = [];
|
|
364
|
+
const delFiles = [];
|
|
365
|
+
for (const [file, newHash] of Object.entries(newStore)) {
|
|
366
|
+
if (!oldStore[file]) {
|
|
367
|
+
newFiles.push(file);
|
|
368
|
+
}
|
|
369
|
+
else if (oldStore[file] !== newHash) {
|
|
370
|
+
drifted.push({ file, old_hash: oldStore[file], new_hash: newHash });
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
for (const file of Object.keys(oldStore)) {
|
|
374
|
+
if (!newStore[file])
|
|
375
|
+
delFiles.push(file);
|
|
376
|
+
}
|
|
377
|
+
const report = {
|
|
378
|
+
ts,
|
|
379
|
+
scan_dir: scanDir,
|
|
380
|
+
total_files: Object.keys(newStore).length,
|
|
381
|
+
drifted,
|
|
382
|
+
new_files: newFiles,
|
|
383
|
+
deleted_files: delFiles,
|
|
384
|
+
};
|
|
385
|
+
appendLog({ event: 'integrity_report', ...report });
|
|
386
|
+
if (drifted.length > 0 || delFiles.length > 0) {
|
|
387
|
+
sendTelegramAlert(`🔍 *[OMEL WipeWitness] Integrity Drift*\n` +
|
|
388
|
+
`Drifted: ${drifted.length} files\n` +
|
|
389
|
+
`Deleted: ${delFiles.length} files\n` +
|
|
390
|
+
`New: ${newFiles.length} files\n` +
|
|
391
|
+
`Run: \`npx ts-node scripts/lib/omel/wipe-witness.ts report\` for details.`);
|
|
392
|
+
}
|
|
393
|
+
return report;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
exports.WipeWitness = WipeWitness;
|
|
397
|
+
// Exported singleton
|
|
398
|
+
exports.wipeWitness = new WipeWitness();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env ts-node
|
|
2
|
+
/**
|
|
3
|
+
* Invoica Agent Orchestrator v2
|
|
4
|
+
*
|
|
5
|
+
* Dynamic Agent Architecture:
|
|
6
|
+
* Leadership (Claude via Anthropic API):
|
|
7
|
+
* - CEO: Sprint planning, strategy, decisions, daily reports
|
|
8
|
+
* - Supervisor 1: Code review & quality gate (DeepSeek via ClawRouter,
|
|
9
|
+
* Claude Sonnet 4.6 for high-stakes; was OpenAI Codex originally)
|
|
10
|
+
* - Supervisor 2: Code review & quality gate (Claude Haiku 4.5,
|
|
11
|
+
* DeepSeek via ClawRouter as drain fallback; was OpenAI Codex originally)
|
|
12
|
+
* - Skills: Agent/skill factory
|
|
13
|
+
* Marketing (Manus AI — runs independently):
|
|
14
|
+
* - CMO: Brand strategy, market intelligence, product proposals (reports loaded from files)
|
|
15
|
+
* Technology (MiniMax M2.5):
|
|
16
|
+
* - CTO: Data-driven analysis, OpenClaw/ClawHub monitoring, agent spawning proposals
|
|
17
|
+
* Execution (MiniMax M2.5 — dynamically loaded from agents/ directory):
|
|
18
|
+
* - Coding agents auto-discovered from agents/{name}/prompt.md
|
|
19
|
+
* - New agents created by CTO proposals + CEO approval
|
|
20
|
+
*
|
|
21
|
+
* Flow: CEO plans → MiniMax codes → Dual Supervisor review (DeepSeek + Haiku)
|
|
22
|
+
* → CEO resolves conflicts → CTO analyzes → CMO reports → CEO daily report
|
|
23
|
+
*/
|
|
24
|
+
declare function main(): Promise<void>;
|
|
25
|
+
export { main as runOrchestrator };
|