@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,324 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// OMEL Credential Vault — Sprint 177 + Sprint 182 Hardening (AMD-13)
|
|
3
|
+
// Controlled access to secrets from process.env.
|
|
4
|
+
// NEVER logs secret values — only key names and masked representations.
|
|
5
|
+
//
|
|
6
|
+
// Public API:
|
|
7
|
+
// getSecret(key) — reads from process.env, NEVER logs value
|
|
8
|
+
// listSecrets() — returns key NAMES only, never values
|
|
9
|
+
// hasSecret(key) — boolean presence check
|
|
10
|
+
// maskForLog(value) — "sk-****...****" pattern for safe logging
|
|
11
|
+
// scanForLeaks(line) — detect verbatim secret values in a log line
|
|
12
|
+
// audit() — CTO pre-check report: all secrets status
|
|
13
|
+
//
|
|
14
|
+
// Sprint 182 hardening additions:
|
|
15
|
+
// - Secret rotation detection: alert if key unchanged for >90 days
|
|
16
|
+
// - Passive log scanner: scan last 100 log lines per orchestrator run
|
|
17
|
+
// - Access rate limiting: same key accessed >50× in 60s → alert
|
|
18
|
+
// - Required secrets check: startup validation of critical env vars
|
|
19
|
+
// - audit() report method for CTO approval gate
|
|
20
|
+
//
|
|
21
|
+
// Audit log: logs/omel/vault-access-YYYY-MM-DD.jsonl (key name + agent_id, never value)
|
|
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.credentialVault = exports.CredentialVault = 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 LOGS_DIR = path.join((0, engine_paths_1.resolveEnginePaths)().root, 'logs', 'omel');
|
|
62
|
+
const VAULT_META = path.join(LOGS_DIR, 'vault-rotation-meta.json');
|
|
63
|
+
const AUDIT_FILE = () => {
|
|
64
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
65
|
+
return path.join(LOGS_DIR, `vault-access-${date}.jsonl`);
|
|
66
|
+
};
|
|
67
|
+
fs.mkdirSync(LOGS_DIR, { recursive: true });
|
|
68
|
+
// ── Telegram fire-and-forget ──────────────────────────────────────────────────
|
|
69
|
+
function sendTelegramAlert(message) {
|
|
70
|
+
const botToken = process.env.TELEGRAM_BOT_TOKEN || '';
|
|
71
|
+
const chatId = process.env.OWNER_TELEGRAM_CHAT_ID || '';
|
|
72
|
+
if (!botToken || !chatId)
|
|
73
|
+
return;
|
|
74
|
+
const payload = JSON.stringify({ chat_id: chatId, text: message, parse_mode: 'Markdown' });
|
|
75
|
+
try {
|
|
76
|
+
const req = https.request({
|
|
77
|
+
hostname: 'api.telegram.org',
|
|
78
|
+
path: `/bot${botToken}/sendMessage`,
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
|
|
81
|
+
});
|
|
82
|
+
req.on('error', () => { });
|
|
83
|
+
req.write(payload);
|
|
84
|
+
req.end();
|
|
85
|
+
}
|
|
86
|
+
catch { /* silent */ }
|
|
87
|
+
}
|
|
88
|
+
// ── Known secret key prefixes for passive leak scanner ───────────────────────
|
|
89
|
+
const SECRET_KEY_PATTERNS = [
|
|
90
|
+
/^ANTHROPIC_API_KEY$/,
|
|
91
|
+
/^OPENAI_API_KEY$/,
|
|
92
|
+
/^TELEGRAM_BOT_TOKEN$/,
|
|
93
|
+
/^CEO_TELEGRAM_BOT_TOKEN$/,
|
|
94
|
+
/^STRIPE_SECRET_KEY$/,
|
|
95
|
+
/^STRIPE_WEBHOOK_SECRET$/,
|
|
96
|
+
/^TIKTOK_ACCESS_TOKEN$/,
|
|
97
|
+
/.*_API_KEY$/,
|
|
98
|
+
/.*_SECRET$/,
|
|
99
|
+
/.*_TOKEN$/,
|
|
100
|
+
/.*_PASSWORD$/,
|
|
101
|
+
/.*_PRIVATE_KEY$/,
|
|
102
|
+
];
|
|
103
|
+
// Required secrets that must be present at startup
|
|
104
|
+
const REQUIRED_SECRETS = [
|
|
105
|
+
'TELEGRAM_BOT_TOKEN',
|
|
106
|
+
'OWNER_TELEGRAM_CHAT_ID',
|
|
107
|
+
];
|
|
108
|
+
// ── Collect known secret keys ─────────────────────────────────────────────────
|
|
109
|
+
function collectSecretKeys() {
|
|
110
|
+
return Object.keys(process.env).filter(k => SECRET_KEY_PATTERNS.some(p => p.test(k)));
|
|
111
|
+
}
|
|
112
|
+
// ── Audit log ─────────────────────────────────────────────────────────────────
|
|
113
|
+
function auditLog(key, agentId, action) {
|
|
114
|
+
const entry = {
|
|
115
|
+
ts: new Date().toISOString(),
|
|
116
|
+
key,
|
|
117
|
+
agent_id: agentId,
|
|
118
|
+
action,
|
|
119
|
+
};
|
|
120
|
+
try {
|
|
121
|
+
fs.appendFileSync(AUDIT_FILE(), JSON.stringify(entry) + '\n');
|
|
122
|
+
}
|
|
123
|
+
catch { /* if log write fails, never crash caller */ }
|
|
124
|
+
}
|
|
125
|
+
function loadRotationMeta() {
|
|
126
|
+
try {
|
|
127
|
+
if (fs.existsSync(VAULT_META)) {
|
|
128
|
+
return JSON.parse(fs.readFileSync(VAULT_META, 'utf-8'));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch { /* ignore */ }
|
|
132
|
+
return {};
|
|
133
|
+
}
|
|
134
|
+
function saveRotationMeta(meta) {
|
|
135
|
+
try {
|
|
136
|
+
fs.writeFileSync(VAULT_META, JSON.stringify(meta, null, 2), 'utf-8');
|
|
137
|
+
}
|
|
138
|
+
catch { /* non-fatal */ }
|
|
139
|
+
}
|
|
140
|
+
function hashSecret(value) {
|
|
141
|
+
// Simple FNV-1a hash for rotation detection (not cryptographic, just drift detection)
|
|
142
|
+
let h = 2166136261;
|
|
143
|
+
for (let i = 0; i < value.length; i++) {
|
|
144
|
+
h ^= value.charCodeAt(i);
|
|
145
|
+
h = (h * 16777619) >>> 0;
|
|
146
|
+
}
|
|
147
|
+
return h.toString(16);
|
|
148
|
+
}
|
|
149
|
+
const RATE_LIMIT_MAX = 50; // accesses per window
|
|
150
|
+
const RATE_LIMIT_WINDOW = 60_000; // 60 seconds
|
|
151
|
+
// ── CredentialVault class ─────────────────────────────────────────────────────
|
|
152
|
+
class CredentialVault {
|
|
153
|
+
rateCounters = new Map();
|
|
154
|
+
// ── Rate limiting helper ────────────────────────────────────────────────────
|
|
155
|
+
checkRateLimit(key, agentId) {
|
|
156
|
+
const now = Date.now();
|
|
157
|
+
const win = this.rateCounters.get(key);
|
|
158
|
+
if (!win || now - win.windowStart > RATE_LIMIT_WINDOW) {
|
|
159
|
+
// Start fresh window
|
|
160
|
+
this.rateCounters.set(key, { count: 1, windowStart: now });
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
win.count++;
|
|
164
|
+
if (win.count > RATE_LIMIT_MAX) {
|
|
165
|
+
auditLog(key, agentId, 'rate_limit');
|
|
166
|
+
sendTelegramAlert(`⚠️ *[OMEL Vault] Rate Limit Alert*\n` +
|
|
167
|
+
`Key: \`${key}\` accessed ${win.count}× in 60s by \`${agentId}\`\n` +
|
|
168
|
+
`Possible infinite loop or runaway agent.`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// ── Rotation detection ──────────────────────────────────────────────────────
|
|
172
|
+
checkRotation(key, value) {
|
|
173
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
174
|
+
const meta = loadRotationMeta();
|
|
175
|
+
const h = hashSecret(value);
|
|
176
|
+
if (!meta[key]) {
|
|
177
|
+
meta[key] = { last_seen_date: today, hash: h };
|
|
178
|
+
saveRotationMeta(meta);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (meta[key].hash !== h) {
|
|
182
|
+
// Secret rotated — update record
|
|
183
|
+
meta[key] = { last_seen_date: today, hash: h };
|
|
184
|
+
saveRotationMeta(meta);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
// Same hash — check age
|
|
188
|
+
const daysSince = Math.floor((Date.now() - new Date(meta[key].last_seen_date).getTime()) / 86_400_000);
|
|
189
|
+
if (daysSince > 90) {
|
|
190
|
+
auditLog(key, 'system', 'rotation_alert');
|
|
191
|
+
sendTelegramAlert(`🔑 *[OMEL Vault] Rotation Alert*\n` +
|
|
192
|
+
`Key: \`${key}\` has not been rotated in *${daysSince} days* (threshold: 90)\n` +
|
|
193
|
+
`Consider rotating this secret.`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Read a secret from process.env.
|
|
198
|
+
* The value is returned to the caller but NEVER written to logs.
|
|
199
|
+
*/
|
|
200
|
+
getSecret(key, agentId = 'orchestrator') {
|
|
201
|
+
auditLog(key, agentId, 'get');
|
|
202
|
+
this.checkRateLimit(key, agentId);
|
|
203
|
+
const value = process.env[key] ?? '';
|
|
204
|
+
if (value)
|
|
205
|
+
this.checkRotation(key, value);
|
|
206
|
+
return value;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Return the NAMES of all env variables matching known secret patterns.
|
|
210
|
+
* Values are NEVER returned.
|
|
211
|
+
*/
|
|
212
|
+
listSecrets(agentId = 'orchestrator') {
|
|
213
|
+
const keys = collectSecretKeys();
|
|
214
|
+
auditLog('__all__', agentId, 'list');
|
|
215
|
+
return keys;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Check if a secret is present (non-empty).
|
|
219
|
+
*/
|
|
220
|
+
hasSecret(key, agentId = 'orchestrator') {
|
|
221
|
+
auditLog(key, agentId, 'check');
|
|
222
|
+
return Boolean(process.env[key]);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Mask a secret value for safe inclusion in logs or Telegram messages.
|
|
226
|
+
* Format: first 4 chars + "****...****" + last 4 chars.
|
|
227
|
+
*/
|
|
228
|
+
maskForLog(value) {
|
|
229
|
+
if (!value)
|
|
230
|
+
return '****';
|
|
231
|
+
if (value.length <= 8)
|
|
232
|
+
return '****';
|
|
233
|
+
const prefix = value.slice(0, 4);
|
|
234
|
+
const suffix = value.slice(-4);
|
|
235
|
+
return `${prefix}****...****${suffix}`;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Passive leak scanner: check if any known secret value appears verbatim
|
|
239
|
+
* in the given log line. Returns the leaked key names (never the values).
|
|
240
|
+
*/
|
|
241
|
+
scanForLeaks(line) {
|
|
242
|
+
const leaked = [];
|
|
243
|
+
for (const key of collectSecretKeys()) {
|
|
244
|
+
const value = process.env[key];
|
|
245
|
+
if (value && value.length >= 8 && line.includes(value)) {
|
|
246
|
+
leaked.push(key);
|
|
247
|
+
process.stderr.write(`[OMEL-VAULT] ⚠️ Secret value for ${key} detected in log line — redact before writing!\n`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return leaked;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Scan last N lines of a log file for secret leaks.
|
|
254
|
+
* Returns list of (key, line_number) tuples where leaks were detected.
|
|
255
|
+
* Intended for use by CTO gate pre-check and orchestrator run startup.
|
|
256
|
+
*/
|
|
257
|
+
scanLogFile(logFilePath, lastNLines = 100) {
|
|
258
|
+
const results = [];
|
|
259
|
+
try {
|
|
260
|
+
if (!fs.existsSync(logFilePath))
|
|
261
|
+
return results;
|
|
262
|
+
const lines = fs.readFileSync(logFilePath, 'utf-8')
|
|
263
|
+
.split('\n').filter(l => l.trim());
|
|
264
|
+
const slice = lines.slice(-lastNLines);
|
|
265
|
+
slice.forEach((line, i) => {
|
|
266
|
+
const leaked = this.scanForLeaks(line);
|
|
267
|
+
for (const key of leaked)
|
|
268
|
+
results.push({ key, lineIndex: i });
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
catch { /* non-fatal */ }
|
|
272
|
+
return results;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Startup required-secrets validation.
|
|
276
|
+
* Logs + throws if any required key is missing.
|
|
277
|
+
* Call once at orchestrator startup.
|
|
278
|
+
*/
|
|
279
|
+
validateRequired(requiredKeys = REQUIRED_SECRETS) {
|
|
280
|
+
const missing = requiredKeys.filter(k => !process.env[k]);
|
|
281
|
+
if (missing.length === 0)
|
|
282
|
+
return;
|
|
283
|
+
const msg = `[OMEL-VAULT] Missing required secrets: ${missing.join(', ')}`;
|
|
284
|
+
process.stderr.write(msg + '\n');
|
|
285
|
+
try {
|
|
286
|
+
fs.appendFileSync(AUDIT_FILE(), JSON.stringify({
|
|
287
|
+
ts: new Date().toISOString(),
|
|
288
|
+
action: 'startup_validation_failed',
|
|
289
|
+
missing,
|
|
290
|
+
}) + '\n');
|
|
291
|
+
}
|
|
292
|
+
catch { /* ignore */ }
|
|
293
|
+
throw new Error(msg);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* CTO approval gate audit report.
|
|
297
|
+
* Returns a summary of vault state: which secrets are present, rotation status.
|
|
298
|
+
* Values are NEVER included.
|
|
299
|
+
*/
|
|
300
|
+
audit() {
|
|
301
|
+
const keys = collectSecretKeys();
|
|
302
|
+
const present = keys.filter(k => Boolean(process.env[k]));
|
|
303
|
+
const missing = REQUIRED_SECRETS.filter(k => !process.env[k]);
|
|
304
|
+
const rotationWarnings = [];
|
|
305
|
+
const meta = loadRotationMeta();
|
|
306
|
+
for (const key of present) {
|
|
307
|
+
if (meta[key]) {
|
|
308
|
+
const daysSince = Math.floor((Date.now() - new Date(meta[key].last_seen_date).getTime()) / 86_400_000);
|
|
309
|
+
if (daysSince > 90)
|
|
310
|
+
rotationWarnings.push(`${key} (${daysSince}d)`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
total_secrets: keys.length,
|
|
315
|
+
present,
|
|
316
|
+
missing,
|
|
317
|
+
rotation_warnings: rotationWarnings,
|
|
318
|
+
generated_at: new Date().toISOString(),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
exports.CredentialVault = CredentialVault;
|
|
323
|
+
// Exported singleton
|
|
324
|
+
exports.credentialVault = new CredentialVault();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type HighRiskOp = 'file_delete' | 'git_reset_hard' | 'env_change' | 'schema_migration' | 'bulk_overwrite' | 'new_file';
|
|
2
|
+
export interface ApprovalResult {
|
|
3
|
+
approved: boolean;
|
|
4
|
+
approvedBy?: string;
|
|
5
|
+
reason?: string;
|
|
6
|
+
ts: string;
|
|
7
|
+
risk_score?: number;
|
|
8
|
+
response_ms?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class HumanBrake {
|
|
11
|
+
/**
|
|
12
|
+
* Returns true if operation is a known HighRiskOp type,
|
|
13
|
+
* OR if context.filePath touches protected files.
|
|
14
|
+
*/
|
|
15
|
+
isHighRisk(operation: string, context?: any): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Returns a risk score 1-10 for the given operation + context.
|
|
18
|
+
* High (8-10): requires approval. Medium (5-7): log only. Low (1-4): silent pass.
|
|
19
|
+
*
|
|
20
|
+
* Context fields considered:
|
|
21
|
+
* - filePath: if it's an orchestrator core file, score bumped to max
|
|
22
|
+
* - size: bulk ops on large files get higher score
|
|
23
|
+
*/
|
|
24
|
+
getRiskScore(operation: string, context?: any): number;
|
|
25
|
+
/**
|
|
26
|
+
* Send Telegram approval request and poll for up to 5 minutes.
|
|
27
|
+
* Returns ApprovalResult: approved on 'yes'/'approve', rejected on other reply or timeout.
|
|
28
|
+
* Logs response time and emits AAR entry (Sprint 184).
|
|
29
|
+
*/
|
|
30
|
+
requireApproval(operation: HighRiskOp, context?: any): Promise<ApprovalResult>;
|
|
31
|
+
}
|
|
32
|
+
export declare const humanBrake: HumanBrake;
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// OMEL Human Brake — Sprint 179 + Sprint 184 Hardening (AMD-13)
|
|
3
|
+
// Human-in-the-loop safety gate for high-risk operations.
|
|
4
|
+
// Telegram approval flow: send notification → poll getUpdates up to 5 min.
|
|
5
|
+
// ABORT on timeout or non-approval reply. JSONL audit trail.
|
|
6
|
+
//
|
|
7
|
+
// API:
|
|
8
|
+
// requireApproval(operation: HighRiskOp): Promise<ApprovalResult>
|
|
9
|
+
// isHighRisk(operation: string, context: any): boolean
|
|
10
|
+
// getRiskScore(operation: string, context: any): number (Sprint 184)
|
|
11
|
+
//
|
|
12
|
+
// Sprint 184 hardening additions:
|
|
13
|
+
// - Risk scoring: getRiskScore() → 1-10 scale
|
|
14
|
+
// - Operation risk table: file_delete=9, git_reset_hard=10, new_file=2, etc.
|
|
15
|
+
// - Approval response time logged per operation
|
|
16
|
+
// - HUMAN_BRAKE_DISABLED=true triggers daily Telegram reminder
|
|
17
|
+
// - AARMiddleware integration: approved/rejected ops get AAR entry
|
|
18
|
+
//
|
|
19
|
+
// Audit log: logs/omel/human-brake-YYYY-MM-DD.jsonl
|
|
20
|
+
// Env: TELEGRAM_BOT_TOKEN, OWNER_TELEGRAM_CHAT_ID, HUMAN_BRAKE_DISABLED
|
|
21
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
24
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
25
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
26
|
+
}
|
|
27
|
+
Object.defineProperty(o, k2, desc);
|
|
28
|
+
}) : (function(o, m, k, k2) {
|
|
29
|
+
if (k2 === undefined) k2 = k;
|
|
30
|
+
o[k2] = m[k];
|
|
31
|
+
}));
|
|
32
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
33
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
34
|
+
}) : function(o, v) {
|
|
35
|
+
o["default"] = v;
|
|
36
|
+
});
|
|
37
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
38
|
+
var ownKeys = function(o) {
|
|
39
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
40
|
+
var ar = [];
|
|
41
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
42
|
+
return ar;
|
|
43
|
+
};
|
|
44
|
+
return ownKeys(o);
|
|
45
|
+
};
|
|
46
|
+
return function (mod) {
|
|
47
|
+
if (mod && mod.__esModule) return mod;
|
|
48
|
+
var result = {};
|
|
49
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
50
|
+
__setModuleDefault(result, mod);
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
53
|
+
})();
|
|
54
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
|
+
exports.humanBrake = exports.HumanBrake = void 0;
|
|
56
|
+
const fs = __importStar(require("fs"));
|
|
57
|
+
const path = __importStar(require("path"));
|
|
58
|
+
const engine_paths_1 = require("../engine-paths");
|
|
59
|
+
const https = __importStar(require("https"));
|
|
60
|
+
// ── Risk score table (Sprint 184) ─────────────────────────────────────────────
|
|
61
|
+
const RISK_SCORES = {
|
|
62
|
+
git_reset_hard: 10,
|
|
63
|
+
file_delete: 9,
|
|
64
|
+
env_change: 8,
|
|
65
|
+
schema_migration: 8,
|
|
66
|
+
bulk_overwrite: 7,
|
|
67
|
+
new_file: 2,
|
|
68
|
+
};
|
|
69
|
+
// ── Logging ───────────────────────────────────────────────────────────────────
|
|
70
|
+
const LOGS_DIR = path.join((0, engine_paths_1.resolveEnginePaths)().root, 'logs', 'omel');
|
|
71
|
+
fs.mkdirSync(LOGS_DIR, { recursive: true });
|
|
72
|
+
function logFile() {
|
|
73
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
74
|
+
return path.join(LOGS_DIR, `human-brake-${date}.jsonl`);
|
|
75
|
+
}
|
|
76
|
+
function appendLog(entry) {
|
|
77
|
+
try {
|
|
78
|
+
fs.appendFileSync(logFile(), JSON.stringify(entry) + '\n');
|
|
79
|
+
}
|
|
80
|
+
catch { /* never crash caller */ }
|
|
81
|
+
}
|
|
82
|
+
// ── AARMiddleware integration (Sprint 184) ────────────────────────────────────
|
|
83
|
+
// Lazy import to avoid circular dependency — AARMiddleware may import OMEL
|
|
84
|
+
function writeAAREntry(operation, approved, risk_score, response_ms) {
|
|
85
|
+
try {
|
|
86
|
+
const aarDir = path.join((0, engine_paths_1.resolveEnginePaths)().root, 'logs', 'aar');
|
|
87
|
+
fs.mkdirSync(aarDir, { recursive: true });
|
|
88
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
89
|
+
const aarFile = path.join(aarDir, `${date}.jsonl`);
|
|
90
|
+
const entry = {
|
|
91
|
+
ts: new Date().toISOString(),
|
|
92
|
+
source: 'omel_human_brake',
|
|
93
|
+
operation,
|
|
94
|
+
approved,
|
|
95
|
+
risk_score,
|
|
96
|
+
response_ms,
|
|
97
|
+
receipt_hash: Buffer.from(`${operation}${Date.now()}`).toString('base64').slice(0, 32),
|
|
98
|
+
};
|
|
99
|
+
fs.appendFileSync(aarFile, JSON.stringify(entry) + '\n');
|
|
100
|
+
}
|
|
101
|
+
catch { /* non-fatal */ }
|
|
102
|
+
}
|
|
103
|
+
// ── Telegram helpers ──────────────────────────────────────────────────────────
|
|
104
|
+
const HIGH_RISK_OP_SET = new Set([
|
|
105
|
+
'file_delete', 'git_reset_hard', 'env_change', 'schema_migration', 'bulk_overwrite',
|
|
106
|
+
]);
|
|
107
|
+
const HIGH_RISK_PATH_FRAGMENTS = ['orchestrate-agents', 'clawrouter', '.env'];
|
|
108
|
+
function sendTelegram(message) {
|
|
109
|
+
const botToken = process.env.TELEGRAM_BOT_TOKEN || '';
|
|
110
|
+
const chatId = process.env.OWNER_TELEGRAM_CHAT_ID || '';
|
|
111
|
+
if (!botToken || !chatId)
|
|
112
|
+
return;
|
|
113
|
+
const payload = JSON.stringify({ chat_id: chatId, text: message, parse_mode: 'Markdown' });
|
|
114
|
+
try {
|
|
115
|
+
const req = https.request({
|
|
116
|
+
hostname: 'api.telegram.org',
|
|
117
|
+
path: `/bot${botToken}/sendMessage`,
|
|
118
|
+
method: 'POST',
|
|
119
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
|
|
120
|
+
});
|
|
121
|
+
req.on('error', () => { }); // fire-and-forget, never crash
|
|
122
|
+
req.write(payload);
|
|
123
|
+
req.end();
|
|
124
|
+
}
|
|
125
|
+
catch { /* silent */ }
|
|
126
|
+
}
|
|
127
|
+
function getUpdates(offset, longPollSeconds) {
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
const botToken = process.env.TELEGRAM_BOT_TOKEN || '';
|
|
130
|
+
if (!botToken) {
|
|
131
|
+
resolve([]);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const req = https.request({
|
|
135
|
+
hostname: 'api.telegram.org',
|
|
136
|
+
path: `/bot${botToken}/getUpdates?offset=${offset}&timeout=${longPollSeconds}`,
|
|
137
|
+
method: 'GET',
|
|
138
|
+
}, (res) => {
|
|
139
|
+
let raw = '';
|
|
140
|
+
res.on('data', (chunk) => { raw += chunk; });
|
|
141
|
+
res.on('end', () => {
|
|
142
|
+
try {
|
|
143
|
+
const parsed = JSON.parse(raw);
|
|
144
|
+
resolve(parsed.ok ? parsed.result : []);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
resolve([]);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
req.on('error', () => resolve([]));
|
|
152
|
+
req.setTimeout((longPollSeconds + 10) * 1000, () => { req.destroy(); resolve([]); });
|
|
153
|
+
req.end();
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// ── HUMAN_BRAKE_DISABLED daily reminder (Sprint 184) ─────────────────────────
|
|
157
|
+
const DISABLED_REMINDER_FILE = path.join(LOGS_DIR, 'brake-disabled-reminder.json');
|
|
158
|
+
function sendDisabledReminderIfNeeded() {
|
|
159
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
160
|
+
try {
|
|
161
|
+
let lastSent = '';
|
|
162
|
+
if (fs.existsSync(DISABLED_REMINDER_FILE)) {
|
|
163
|
+
lastSent = JSON.parse(fs.readFileSync(DISABLED_REMINDER_FILE, 'utf-8')).last_sent ?? '';
|
|
164
|
+
}
|
|
165
|
+
if (lastSent === today)
|
|
166
|
+
return; // already sent today
|
|
167
|
+
sendTelegram(`⚠️ *[OMEL HumanBrake] Brake DISABLED*\n` +
|
|
168
|
+
`\`HUMAN_BRAKE_DISABLED=true\` is set in your environment.\n` +
|
|
169
|
+
`High-risk operations will NOT require approval.\n` +
|
|
170
|
+
`This reminder fires daily until the flag is removed.`);
|
|
171
|
+
fs.writeFileSync(DISABLED_REMINDER_FILE, JSON.stringify({ last_sent: today }), 'utf-8');
|
|
172
|
+
}
|
|
173
|
+
catch { /* non-fatal */ }
|
|
174
|
+
}
|
|
175
|
+
// ── HumanBrake class ──────────────────────────────────────────────────────────
|
|
176
|
+
class HumanBrake {
|
|
177
|
+
/**
|
|
178
|
+
* Returns true if operation is a known HighRiskOp type,
|
|
179
|
+
* OR if context.filePath touches protected files.
|
|
180
|
+
*/
|
|
181
|
+
isHighRisk(operation, context = {}) {
|
|
182
|
+
// Gate by the score-threshold the docstring on getRiskScore already
|
|
183
|
+
// promises ("High (8-10): requires approval"). Catches file_delete (9),
|
|
184
|
+
// git_reset_hard (10), env_change (8), schema_migration (8), and any
|
|
185
|
+
// op bumped to ≥8 by HIGH_RISK_PATH_FRAGMENTS or size context.
|
|
186
|
+
// Drops bulk_overwrite (7) — was sweep-catching every routine source
|
|
187
|
+
// edit and forcing a 5-min Telegram approval wait per task, blocking
|
|
188
|
+
// autonomous swarm runs.
|
|
189
|
+
return this.getRiskScore(operation, context) >= 8;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Returns a risk score 1-10 for the given operation + context.
|
|
193
|
+
* High (8-10): requires approval. Medium (5-7): log only. Low (1-4): silent pass.
|
|
194
|
+
*
|
|
195
|
+
* Context fields considered:
|
|
196
|
+
* - filePath: if it's an orchestrator core file, score bumped to max
|
|
197
|
+
* - size: bulk ops on large files get higher score
|
|
198
|
+
*/
|
|
199
|
+
getRiskScore(operation, context = {}) {
|
|
200
|
+
let score = RISK_SCORES[operation] ?? 5; // default 5 (medium) for unknown ops
|
|
201
|
+
// Bump to 10 if touching core orchestrator or config files
|
|
202
|
+
const filePath = String(context.filePath || '');
|
|
203
|
+
if (HIGH_RISK_PATH_FRAGMENTS.some(f => filePath.includes(f))) {
|
|
204
|
+
score = Math.max(score, 9);
|
|
205
|
+
}
|
|
206
|
+
// Bump for large file bulk ops
|
|
207
|
+
const sizeBytes = Number(context.sizeBytes || 0);
|
|
208
|
+
if (operation === 'bulk_overwrite' && sizeBytes > 50_000) {
|
|
209
|
+
score = Math.max(score, 9);
|
|
210
|
+
}
|
|
211
|
+
return Math.min(10, Math.max(1, score));
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Send Telegram approval request and poll for up to 5 minutes.
|
|
215
|
+
* Returns ApprovalResult: approved on 'yes'/'approve', rejected on other reply or timeout.
|
|
216
|
+
* Logs response time and emits AAR entry (Sprint 184).
|
|
217
|
+
*/
|
|
218
|
+
async requireApproval(operation, context = {}) {
|
|
219
|
+
const ts = new Date().toISOString();
|
|
220
|
+
const risk_score = this.getRiskScore(operation, context);
|
|
221
|
+
const requestedAt = Date.now();
|
|
222
|
+
// ── HUMAN_BRAKE_DISABLED bypass ──────────────────────────────────────────
|
|
223
|
+
if (process.env.HUMAN_BRAKE_DISABLED === 'true') {
|
|
224
|
+
sendDisabledReminderIfNeeded();
|
|
225
|
+
const response_ms = Date.now() - requestedAt;
|
|
226
|
+
appendLog({ event: 'brake_disabled', operation, risk_score, response_ms, ts });
|
|
227
|
+
writeAAREntry(operation, true, risk_score, response_ms);
|
|
228
|
+
return { approved: true, approvedBy: 'HUMAN_BRAKE_DISABLED', risk_score, response_ms, ts };
|
|
229
|
+
}
|
|
230
|
+
// ── Notify operator via Telegram ─────────────────────────────────────────
|
|
231
|
+
sendTelegram(`🛑 *[OMEL HumanBrake] Approval Required*\n` +
|
|
232
|
+
`Operation: \`${operation}\`\n` +
|
|
233
|
+
`Risk Score: *${risk_score}/10*\n` +
|
|
234
|
+
`Requested: ${ts}\n\n` +
|
|
235
|
+
`Reply *yes* or *approve* to allow.\n` +
|
|
236
|
+
`Any other reply or 5-minute timeout = ABORT.`);
|
|
237
|
+
appendLog({ event: 'approval_requested', operation, risk_score, ts });
|
|
238
|
+
// ── Poll Telegram for up to 5 minutes ────────────────────────────────────
|
|
239
|
+
const chatId = process.env.OWNER_TELEGRAM_CHAT_ID || '';
|
|
240
|
+
const deadline = Date.now() + 5 * 60 * 1000;
|
|
241
|
+
let offset = 0;
|
|
242
|
+
while (Date.now() < deadline) {
|
|
243
|
+
const remainingMs = deadline - Date.now();
|
|
244
|
+
if (remainingMs <= 0)
|
|
245
|
+
break;
|
|
246
|
+
const pollSecs = Math.min(30, Math.floor(remainingMs / 1000));
|
|
247
|
+
if (pollSecs <= 0)
|
|
248
|
+
break;
|
|
249
|
+
const updates = await getUpdates(offset, pollSecs);
|
|
250
|
+
for (const update of updates) {
|
|
251
|
+
if (typeof update.update_id === 'number') {
|
|
252
|
+
offset = update.update_id + 1;
|
|
253
|
+
}
|
|
254
|
+
const msg = update.message || update.edited_message;
|
|
255
|
+
if (!msg)
|
|
256
|
+
continue;
|
|
257
|
+
if (chatId && String(msg.chat?.id) !== chatId)
|
|
258
|
+
continue;
|
|
259
|
+
const text = String(msg.text || '').trim().toLowerCase();
|
|
260
|
+
if (text === 'yes' || text === 'approve') {
|
|
261
|
+
const approvedTs = new Date().toISOString();
|
|
262
|
+
const response_ms = Date.now() - requestedAt;
|
|
263
|
+
appendLog({ event: 'approved', operation, risk_score, approvedBy: 'telegram', response_ms, ts: approvedTs });
|
|
264
|
+
writeAAREntry(operation, true, risk_score, response_ms);
|
|
265
|
+
return { approved: true, approvedBy: 'telegram', risk_score, response_ms, ts: approvedTs };
|
|
266
|
+
}
|
|
267
|
+
// Any non-approval reply → reject immediately
|
|
268
|
+
const rejectedTs = new Date().toISOString();
|
|
269
|
+
const response_ms = Date.now() - requestedAt;
|
|
270
|
+
appendLog({ event: 'rejected', operation, risk_score, reason: 'rejected', replyText: text, response_ms, ts: rejectedTs });
|
|
271
|
+
writeAAREntry(operation, false, risk_score, response_ms);
|
|
272
|
+
sendTelegram(`❌ *[OMEL HumanBrake] ABORTED*\nOperation \`${operation}\` rejected by operator.`);
|
|
273
|
+
return { approved: false, reason: 'rejected', risk_score, response_ms, ts: rejectedTs };
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// ── Timeout ──────────────────────────────────────────────────────────────
|
|
277
|
+
const timeoutTs = new Date().toISOString();
|
|
278
|
+
const response_ms = Date.now() - requestedAt;
|
|
279
|
+
appendLog({ event: 'timeout_abort', operation, risk_score, reason: 'timeout', response_ms, ts: timeoutTs });
|
|
280
|
+
writeAAREntry(operation, false, risk_score, response_ms);
|
|
281
|
+
sendTelegram(`⏰ *[OMEL HumanBrake] TIMEOUT — ABORTED*\n` +
|
|
282
|
+
`Operation \`${operation}\` aborted — no response within 5 minutes.\n` +
|
|
283
|
+
`Risk Score: *${risk_score}/10*`);
|
|
284
|
+
return { approved: false, reason: 'timeout', risk_score, response_ms, ts: timeoutTs };
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
exports.HumanBrake = HumanBrake;
|
|
288
|
+
// ── Singleton export ──────────────────────────────────────────────────────────
|
|
289
|
+
exports.humanBrake = new HumanBrake();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OMEL (Operational Memory & Execution Ledger) safety cluster — Phase 3b-3 Wave B.
|
|
3
|
+
* Summer Yu §8 measures: credential vault, human-brake kill switch, phantom
|
|
4
|
+
* workspace sandbox, wipe-witness tamper ledger. Self-contained, stdlib-only;
|
|
5
|
+
* roots resolved via engine-paths (<root>/logs/omel, <root>/logs/aar).
|
|
6
|
+
*/
|
|
7
|
+
export * from './credential-vault';
|
|
8
|
+
export * from './human-brake';
|
|
9
|
+
export * from './phantom-workspace';
|
|
10
|
+
export * from './wipe-witness';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
/**
|
|
18
|
+
* OMEL (Operational Memory & Execution Ledger) safety cluster — Phase 3b-3 Wave B.
|
|
19
|
+
* Summer Yu §8 measures: credential vault, human-brake kill switch, phantom
|
|
20
|
+
* workspace sandbox, wipe-witness tamper ledger. Self-contained, stdlib-only;
|
|
21
|
+
* roots resolved via engine-paths (<root>/logs/omel, <root>/logs/aar).
|
|
22
|
+
*/
|
|
23
|
+
__exportStar(require("./credential-vault"), exports);
|
|
24
|
+
__exportStar(require("./human-brake"), exports);
|
|
25
|
+
__exportStar(require("./phantom-workspace"), exports);
|
|
26
|
+
__exportStar(require("./wipe-witness"), exports);
|