monora-ai 2.1.0 → 2.1.4
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 +339 -158
- package/dist/aims_governance.d.ts +238 -0
- package/dist/aims_governance.d.ts.map +1 -0
- package/dist/aims_governance.js +922 -0
- package/dist/alerts.d.ts +16 -0
- package/dist/alerts.d.ts.map +1 -1
- package/dist/alerts.js +16 -0
- package/dist/api.d.ts +6 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +6 -0
- package/dist/assessment.d.ts +85 -0
- package/dist/assessment.d.ts.map +1 -1
- package/dist/assessment.js +525 -13
- package/dist/attribution.d.ts +44 -3
- package/dist/attribution.d.ts.map +1 -1
- package/dist/attribution.js +197 -10
- package/dist/autodetect.d.ts +68 -0
- package/dist/autodetect.d.ts.map +1 -1
- package/dist/autodetect.js +639 -0
- package/dist/bias.d.ts +130 -0
- package/dist/bias.d.ts.map +1 -0
- package/dist/bias.js +223 -0
- package/dist/cli/diagnostics.d.ts +5 -1
- package/dist/cli/diagnostics.d.ts.map +1 -1
- package/dist/cli/diagnostics.js +23 -6
- package/dist/cli/doctor.d.ts +25 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +381 -0
- package/dist/cli/fix.d.ts +16 -0
- package/dist/cli/fix.d.ts.map +1 -0
- package/dist/cli/fix.js +284 -0
- package/dist/cli/init.d.ts +57 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +205 -0
- package/dist/cli.js +1564 -177
- package/dist/complianceConsolidation.d.ts +17 -0
- package/dist/complianceConsolidation.d.ts.map +1 -0
- package/dist/complianceConsolidation.js +68 -0
- package/dist/complianceTargets.d.ts +111 -0
- package/dist/complianceTargets.d.ts.map +1 -0
- package/dist/complianceTargets.js +521 -0
- package/dist/config.d.ts +261 -16
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +381 -32
- package/dist/config_migrations.d.ts.map +1 -1
- package/dist/config_migrations.js +38 -1
- package/dist/config_schema.d.ts +2490 -1035
- package/dist/config_schema.d.ts.map +1 -1
- package/dist/config_schema.js +233 -64
- package/dist/context.d.ts +34 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +118 -7
- package/dist/control_backbone.d.ts +128 -0
- package/dist/control_backbone.d.ts.map +1 -0
- package/dist/control_backbone.js +826 -0
- package/dist/data-governance.d.ts +187 -0
- package/dist/data-governance.d.ts.map +1 -0
- package/dist/data-governance.js +424 -0
- package/dist/dataResidency.d.ts +44 -0
- package/dist/dataResidency.d.ts.map +1 -0
- package/dist/dataResidency.js +203 -0
- package/dist/dispatcher.d.ts.map +1 -1
- package/dist/dispatcher.js +17 -5
- package/dist/evidence_store.d.ts +103 -0
- package/dist/evidence_store.d.ts.map +1 -0
- package/dist/evidence_store.js +459 -0
- package/dist/executiveSummary.d.ts +15 -0
- package/dist/executiveSummary.d.ts.map +1 -1
- package/dist/executiveSummary.js +135 -22
- package/dist/identity.d.ts +143 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +231 -0
- package/dist/impact-assessment.d.ts +350 -0
- package/dist/impact-assessment.d.ts.map +1 -0
- package/dist/impact-assessment.js +580 -0
- package/dist/index.d.ts +21 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +254 -5
- package/dist/instrumentation.d.ts +1 -1
- package/dist/instrumentation.d.ts.map +1 -1
- package/dist/instrumentation.js +123 -22
- package/dist/integrations/anthropic.d.ts +3 -0
- package/dist/integrations/anthropic.d.ts.map +1 -1
- package/dist/integrations/anthropic.js +282 -80
- package/dist/integrations/governance.d.ts +33 -0
- package/dist/integrations/governance.d.ts.map +1 -0
- package/dist/integrations/governance.js +208 -0
- package/dist/integrations/langchain.d.ts +4 -0
- package/dist/integrations/langchain.d.ts.map +1 -1
- package/dist/integrations/langchain.js +362 -142
- package/dist/integrations/openai.d.ts +9 -0
- package/dist/integrations/openai.d.ts.map +1 -1
- package/dist/integrations/openai.js +673 -73
- package/dist/iso42001_consolidation.d.ts +16 -0
- package/dist/iso42001_consolidation.d.ts.map +1 -0
- package/dist/iso42001_consolidation.js +413 -0
- package/dist/iso42001_workflows.d.ts +263 -0
- package/dist/iso42001_workflows.d.ts.map +1 -0
- package/dist/iso42001_workflows.js +781 -0
- package/dist/lifecycle.d.ts +299 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +624 -0
- package/dist/lineage.d.ts +2 -2
- package/dist/lineage.d.ts.map +1 -1
- package/dist/lineage.js +9 -16
- package/dist/middleware/express.d.ts.map +1 -1
- package/dist/middleware/express.js +18 -3
- package/dist/middleware/nextjs.js +2 -2
- package/dist/model.d.ts +143 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +371 -0
- package/dist/onboarding.d.ts +42 -0
- package/dist/onboarding.d.ts.map +1 -0
- package/dist/onboarding.js +1075 -0
- package/dist/oversight.d.ts +264 -0
- package/dist/oversight.d.ts.map +1 -0
- package/dist/oversight.js +497 -0
- package/dist/presets.js +7 -7
- package/dist/quotas.d.ts +171 -0
- package/dist/quotas.d.ts.map +1 -0
- package/dist/quotas.js +259 -0
- package/dist/register.d.ts +13 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +99 -0
- package/dist/registry.d.ts +1 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +7 -0
- package/dist/registryData.json +43 -6
- package/dist/report.d.ts +2 -1
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +189 -2
- package/dist/reporting.d.ts +125 -0
- package/dist/reporting.d.ts.map +1 -1
- package/dist/reporting.js +192 -2
- package/dist/resources.d.ts +285 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +643 -0
- package/dist/risk.d.ts +120 -0
- package/dist/risk.d.ts.map +1 -0
- package/dist/risk.js +220 -0
- package/dist/runtime.d.ts +74 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +416 -18
- package/dist/schemaInference.d.ts +92 -0
- package/dist/schemaInference.d.ts.map +1 -0
- package/dist/schemaInference.js +466 -0
- package/dist/schema_validation.js +2 -2
- package/dist/schemas/config.schema.json +118 -4
- package/dist/security_report.js +4 -4
- package/dist/signing.d.ts +1 -1
- package/dist/signing.d.ts.map +1 -1
- package/dist/signing.js +4 -0
- package/dist/sinks/file.d.ts +19 -1
- package/dist/sinks/file.d.ts.map +1 -1
- package/dist/sinks/file.js +82 -13
- package/dist/sinks/https.d.ts +10 -0
- package/dist/sinks/https.d.ts.map +1 -1
- package/dist/sinks/https.js +76 -16
- package/dist/sinks/stdout.d.ts +1 -0
- package/dist/sinks/stdout.d.ts.map +1 -1
- package/dist/sinks/stdout.js +12 -1
- package/dist/spec.d.ts +159 -0
- package/dist/spec.d.ts.map +1 -0
- package/dist/spec.js +391 -0
- package/dist/stakeholders.d.ts +199 -0
- package/dist/stakeholders.d.ts.map +1 -0
- package/dist/stakeholders.js +398 -0
- package/dist/standards.d.ts.map +1 -1
- package/dist/standards.js +160 -2
- package/dist/standards_ingest.d.ts.map +1 -1
- package/dist/standards_ingest.js +1 -4
- package/dist/telemetry.d.ts +16 -2
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +77 -14
- package/dist/templates/controls/gdpr_control_catalog.json +261 -0
- package/dist/templates/controls/iso42001_control_catalog.json +1443 -0
- package/dist/templates/controls/soc2_control_catalog.json +163 -0
- package/dist/templates/standards/iso42001_claims.json +72 -0
- package/dist/traced_emitter.d.ts.map +1 -1
- package/dist/traced_emitter.js +19 -9
- package/dist/trust_package.d.ts +20 -1
- package/dist/trust_package.d.ts.map +1 -1
- package/dist/trust_package.js +90 -2
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +9 -2
- package/dist/wal.d.ts.map +1 -1
- package/dist/wal.js +2 -1
- package/package.json +14 -1
- package/scripts/postinstall.js +105 -210
- package/templates/controls/gdpr_control_catalog.json +261 -0
- package/templates/controls/iso42001_control_catalog.json +1443 -0
- package/templates/controls/soc2_control_catalog.json +163 -0
- package/templates/standards/iso42001_claims.json +72 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Human oversight tracking for EU AI Act Art.14 and ISO 42001 8.3 compliance.
|
|
4
|
+
*
|
|
5
|
+
* This module provides human review and override tracking for AI decisions,
|
|
6
|
+
* supporting EU AI Act Article 14 human oversight requirements and ISO 42001
|
|
7
|
+
* control 8.3.
|
|
8
|
+
*
|
|
9
|
+
* Cross-SDK Parity:
|
|
10
|
+
* Both Python and Node.js SDKs provide identical human oversight APIs:
|
|
11
|
+
* - recordHumanReview() / record_human_review()
|
|
12
|
+
* - recordHumanOverride() / record_human_override()
|
|
13
|
+
* - getPendingReviews() / get_pending_reviews()
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.SqliteReviewStore = exports.MemoryReviewStore = void 0;
|
|
50
|
+
exports.reviewToEventBody = reviewToEventBody;
|
|
51
|
+
exports.overrideToEventBody = overrideToEventBody;
|
|
52
|
+
exports.setReviewStore = setReviewStore;
|
|
53
|
+
exports.recordHumanReview = recordHumanReview;
|
|
54
|
+
exports.recordHumanOverride = recordHumanOverride;
|
|
55
|
+
exports.requireReview = requireReview;
|
|
56
|
+
exports.getPendingReviews = getPendingReviews;
|
|
57
|
+
exports.getTimedOutReviews = getTimedOutReviews;
|
|
58
|
+
exports.checkReviewRequired = checkReviewRequired;
|
|
59
|
+
exports.getOversightSummary = getOversightSummary;
|
|
60
|
+
exports.clearOversightData = clearOversightData;
|
|
61
|
+
const crypto = __importStar(require("crypto"));
|
|
62
|
+
/**
|
|
63
|
+
* Convert HumanReview to event body dictionary.
|
|
64
|
+
*/
|
|
65
|
+
function reviewToEventBody(review) {
|
|
66
|
+
const body = {
|
|
67
|
+
reviewed_event_id: review.reviewedEventId,
|
|
68
|
+
reviewer_id: review.reviewerId,
|
|
69
|
+
review_type: review.reviewType,
|
|
70
|
+
decision: review.decision,
|
|
71
|
+
timestamp: review.timestamp,
|
|
72
|
+
};
|
|
73
|
+
if (review.modifications) {
|
|
74
|
+
body.modifications = review.modifications;
|
|
75
|
+
}
|
|
76
|
+
if (review.reviewDurationMs !== undefined) {
|
|
77
|
+
body.review_duration_ms = review.reviewDurationMs;
|
|
78
|
+
}
|
|
79
|
+
if (review.reviewNotes) {
|
|
80
|
+
body.review_notes = review.reviewNotes;
|
|
81
|
+
}
|
|
82
|
+
if (review.complianceFlags.length > 0) {
|
|
83
|
+
body.compliance_flags = review.complianceFlags;
|
|
84
|
+
}
|
|
85
|
+
return body;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Convert HumanOverride to event body dictionary.
|
|
89
|
+
*/
|
|
90
|
+
function overrideToEventBody(override) {
|
|
91
|
+
const body = {
|
|
92
|
+
overridden_event_id: override.overriddenEventId,
|
|
93
|
+
override_reason: override.overrideReason,
|
|
94
|
+
timestamp: override.timestamp,
|
|
95
|
+
};
|
|
96
|
+
if (override.originalOutputHash) {
|
|
97
|
+
body.original_output_hash = override.originalOutputHash;
|
|
98
|
+
}
|
|
99
|
+
if (override.correctedOutputHash) {
|
|
100
|
+
body.corrected_output_hash = override.correctedOutputHash;
|
|
101
|
+
}
|
|
102
|
+
if (override.overrideAuthority) {
|
|
103
|
+
body.override_authority = override.overrideAuthority;
|
|
104
|
+
}
|
|
105
|
+
return body;
|
|
106
|
+
}
|
|
107
|
+
const DEFAULT_COMPLETED_REVIEW_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
108
|
+
/**
|
|
109
|
+
* In-memory store for oversight reviews.
|
|
110
|
+
*
|
|
111
|
+
* Note: This is single-instance only. For production/distributed use, supply
|
|
112
|
+
* a persistent store via setReviewStore (e.g., SqliteReviewStore or Redis).
|
|
113
|
+
*/
|
|
114
|
+
class MemoryReviewStore {
|
|
115
|
+
constructor(options) {
|
|
116
|
+
this.pendingReviews = new Map();
|
|
117
|
+
this.completedReviews = [];
|
|
118
|
+
this.overrides = [];
|
|
119
|
+
this.completedTtlMs = options?.completedTtlMs ?? DEFAULT_COMPLETED_REVIEW_TTL_MS;
|
|
120
|
+
}
|
|
121
|
+
addPendingReview(review) {
|
|
122
|
+
this.pendingReviews.set(review.eventId, review);
|
|
123
|
+
}
|
|
124
|
+
removePendingReview(eventId) {
|
|
125
|
+
this.pendingReviews.delete(eventId);
|
|
126
|
+
}
|
|
127
|
+
listPendingReviews() {
|
|
128
|
+
return Array.from(this.pendingReviews.values());
|
|
129
|
+
}
|
|
130
|
+
listTimedOutReviews(now) {
|
|
131
|
+
return this.listPendingReviews().filter((review) => review.timeoutAt < now);
|
|
132
|
+
}
|
|
133
|
+
addCompletedReview(review) {
|
|
134
|
+
this.pruneCompletedReviews(Date.now());
|
|
135
|
+
this.completedReviews.push(review);
|
|
136
|
+
}
|
|
137
|
+
listCompletedReviews() {
|
|
138
|
+
this.pruneCompletedReviews(Date.now());
|
|
139
|
+
return [...this.completedReviews];
|
|
140
|
+
}
|
|
141
|
+
addOverride(override) {
|
|
142
|
+
this.overrides.push(override);
|
|
143
|
+
}
|
|
144
|
+
listOverrides() {
|
|
145
|
+
return [...this.overrides];
|
|
146
|
+
}
|
|
147
|
+
clear() {
|
|
148
|
+
this.pendingReviews.clear();
|
|
149
|
+
this.completedReviews = [];
|
|
150
|
+
this.overrides = [];
|
|
151
|
+
}
|
|
152
|
+
pruneCompletedReviews(now) {
|
|
153
|
+
if (this.completedTtlMs <= 0) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const cutoff = now - this.completedTtlMs;
|
|
157
|
+
this.completedReviews = this.completedReviews.filter((review) => {
|
|
158
|
+
const timestampMs = Date.parse(review.timestamp);
|
|
159
|
+
if (Number.isNaN(timestampMs)) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
return timestampMs >= cutoff;
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
exports.MemoryReviewStore = MemoryReviewStore;
|
|
167
|
+
/**
|
|
168
|
+
* SQLite-backed persistent review store (requires better-sqlite3).
|
|
169
|
+
*/
|
|
170
|
+
class SqliteReviewStore {
|
|
171
|
+
constructor(dbPath, options) {
|
|
172
|
+
let Database;
|
|
173
|
+
try {
|
|
174
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
175
|
+
Database = require('better-sqlite3');
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
179
|
+
throw new Error(`SqliteReviewStore requires better-sqlite3. ${message}`);
|
|
180
|
+
}
|
|
181
|
+
this.db = new Database(dbPath);
|
|
182
|
+
this.completedTtlMs = options?.completedTtlMs ?? DEFAULT_COMPLETED_REVIEW_TTL_MS;
|
|
183
|
+
this.db.exec(`
|
|
184
|
+
CREATE TABLE IF NOT EXISTS pending_reviews (
|
|
185
|
+
event_id TEXT PRIMARY KEY,
|
|
186
|
+
trace_id TEXT,
|
|
187
|
+
event_type TEXT,
|
|
188
|
+
model TEXT,
|
|
189
|
+
risk_level TEXT,
|
|
190
|
+
created_at INTEGER,
|
|
191
|
+
timeout_at INTEGER
|
|
192
|
+
);
|
|
193
|
+
CREATE TABLE IF NOT EXISTS completed_reviews (
|
|
194
|
+
reviewed_event_id TEXT,
|
|
195
|
+
reviewer_id TEXT,
|
|
196
|
+
review_type TEXT,
|
|
197
|
+
decision TEXT,
|
|
198
|
+
modifications TEXT,
|
|
199
|
+
review_duration_ms INTEGER,
|
|
200
|
+
review_notes TEXT,
|
|
201
|
+
compliance_flags TEXT,
|
|
202
|
+
timestamp TEXT,
|
|
203
|
+
timestamp_ms INTEGER
|
|
204
|
+
);
|
|
205
|
+
CREATE TABLE IF NOT EXISTS overrides (
|
|
206
|
+
overridden_event_id TEXT,
|
|
207
|
+
override_reason TEXT,
|
|
208
|
+
original_output_hash TEXT,
|
|
209
|
+
corrected_output_hash TEXT,
|
|
210
|
+
override_authority TEXT,
|
|
211
|
+
timestamp TEXT
|
|
212
|
+
);
|
|
213
|
+
`);
|
|
214
|
+
}
|
|
215
|
+
addPendingReview(review) {
|
|
216
|
+
this.db.prepare(`
|
|
217
|
+
INSERT OR REPLACE INTO pending_reviews
|
|
218
|
+
(event_id, trace_id, event_type, model, risk_level, created_at, timeout_at)
|
|
219
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
220
|
+
`).run(review.eventId, review.traceId ?? null, review.eventType, review.model ?? null, review.riskLevel ?? null, review.createdAt, review.timeoutAt);
|
|
221
|
+
}
|
|
222
|
+
removePendingReview(eventId) {
|
|
223
|
+
this.db.prepare('DELETE FROM pending_reviews WHERE event_id = ?').run(eventId);
|
|
224
|
+
}
|
|
225
|
+
listPendingReviews() {
|
|
226
|
+
const rows = this.db.prepare('SELECT * FROM pending_reviews').all();
|
|
227
|
+
return rows.map((row) => ({
|
|
228
|
+
eventId: row.event_id,
|
|
229
|
+
traceId: row.trace_id ?? undefined,
|
|
230
|
+
eventType: row.event_type,
|
|
231
|
+
model: row.model ?? undefined,
|
|
232
|
+
riskLevel: row.risk_level ?? undefined,
|
|
233
|
+
createdAt: row.created_at,
|
|
234
|
+
timeoutAt: row.timeout_at,
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
listTimedOutReviews(now) {
|
|
238
|
+
const rows = this.db
|
|
239
|
+
.prepare('SELECT * FROM pending_reviews WHERE timeout_at < ?')
|
|
240
|
+
.all(now);
|
|
241
|
+
return rows.map((row) => ({
|
|
242
|
+
eventId: row.event_id,
|
|
243
|
+
traceId: row.trace_id ?? undefined,
|
|
244
|
+
eventType: row.event_type,
|
|
245
|
+
model: row.model ?? undefined,
|
|
246
|
+
riskLevel: row.risk_level ?? undefined,
|
|
247
|
+
createdAt: row.created_at,
|
|
248
|
+
timeoutAt: row.timeout_at,
|
|
249
|
+
}));
|
|
250
|
+
}
|
|
251
|
+
addCompletedReview(review) {
|
|
252
|
+
const timestampMs = Date.parse(review.timestamp);
|
|
253
|
+
this.db.prepare(`
|
|
254
|
+
INSERT INTO completed_reviews
|
|
255
|
+
(reviewed_event_id, reviewer_id, review_type, decision, modifications, review_duration_ms,
|
|
256
|
+
review_notes, compliance_flags, timestamp, timestamp_ms)
|
|
257
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
258
|
+
`).run(review.reviewedEventId, review.reviewerId, review.reviewType, review.decision, review.modifications ?? null, review.reviewDurationMs ?? null, review.reviewNotes ?? null, JSON.stringify(review.complianceFlags || []), review.timestamp, Number.isNaN(timestampMs) ? null : timestampMs);
|
|
259
|
+
this.pruneCompletedReviews(Date.now());
|
|
260
|
+
}
|
|
261
|
+
listCompletedReviews() {
|
|
262
|
+
this.pruneCompletedReviews(Date.now());
|
|
263
|
+
const rows = this.db.prepare('SELECT * FROM completed_reviews').all();
|
|
264
|
+
return rows.map((row) => ({
|
|
265
|
+
reviewedEventId: row.reviewed_event_id,
|
|
266
|
+
reviewerId: row.reviewer_id,
|
|
267
|
+
reviewType: row.review_type,
|
|
268
|
+
decision: row.decision,
|
|
269
|
+
modifications: row.modifications ?? undefined,
|
|
270
|
+
reviewDurationMs: row.review_duration_ms ?? undefined,
|
|
271
|
+
reviewNotes: row.review_notes ?? undefined,
|
|
272
|
+
complianceFlags: this.parseComplianceFlags(row.compliance_flags),
|
|
273
|
+
timestamp: row.timestamp,
|
|
274
|
+
}));
|
|
275
|
+
}
|
|
276
|
+
addOverride(override) {
|
|
277
|
+
this.db.prepare(`
|
|
278
|
+
INSERT INTO overrides
|
|
279
|
+
(overridden_event_id, override_reason, original_output_hash, corrected_output_hash,
|
|
280
|
+
override_authority, timestamp)
|
|
281
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
282
|
+
`).run(override.overriddenEventId, override.overrideReason, override.originalOutputHash ?? null, override.correctedOutputHash ?? null, override.overrideAuthority ?? null, override.timestamp);
|
|
283
|
+
}
|
|
284
|
+
listOverrides() {
|
|
285
|
+
const rows = this.db.prepare('SELECT * FROM overrides').all();
|
|
286
|
+
return rows.map((row) => ({
|
|
287
|
+
overriddenEventId: row.overridden_event_id,
|
|
288
|
+
overrideReason: row.override_reason,
|
|
289
|
+
originalOutputHash: row.original_output_hash ?? undefined,
|
|
290
|
+
correctedOutputHash: row.corrected_output_hash ?? undefined,
|
|
291
|
+
overrideAuthority: row.override_authority ?? undefined,
|
|
292
|
+
timestamp: row.timestamp,
|
|
293
|
+
}));
|
|
294
|
+
}
|
|
295
|
+
clear() {
|
|
296
|
+
this.db.exec('DELETE FROM pending_reviews; DELETE FROM completed_reviews; DELETE FROM overrides;');
|
|
297
|
+
}
|
|
298
|
+
pruneCompletedReviews(now) {
|
|
299
|
+
if (this.completedTtlMs <= 0) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const cutoff = now - this.completedTtlMs;
|
|
303
|
+
this.db.prepare('DELETE FROM completed_reviews WHERE timestamp_ms IS NOT NULL AND timestamp_ms < ?')
|
|
304
|
+
.run(cutoff);
|
|
305
|
+
}
|
|
306
|
+
parseComplianceFlags(value) {
|
|
307
|
+
if (!value) {
|
|
308
|
+
return [];
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
const parsed = JSON.parse(value);
|
|
312
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
return [];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
exports.SqliteReviewStore = SqliteReviewStore;
|
|
320
|
+
let reviewStore = new MemoryReviewStore();
|
|
321
|
+
function setReviewStore(store) {
|
|
322
|
+
reviewStore = store;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Record a human review of an AI decision.
|
|
326
|
+
*
|
|
327
|
+
* @param reviewedEventId - ID of the event being reviewed.
|
|
328
|
+
* @param reviewerId - ID of the human reviewer.
|
|
329
|
+
* @param reviewType - Type of review (approval, rejection, modification, escalation).
|
|
330
|
+
* @param decision - Review decision (approved, rejected, modified, escalated).
|
|
331
|
+
* @param options - Additional options.
|
|
332
|
+
* @returns The created HumanReview.
|
|
333
|
+
*
|
|
334
|
+
* @example
|
|
335
|
+
* ```typescript
|
|
336
|
+
* recordHumanReview(
|
|
337
|
+
* 'evt_123',
|
|
338
|
+
* 'usr_456',
|
|
339
|
+
* 'approval',
|
|
340
|
+
* 'approved',
|
|
341
|
+
* { notes: 'Verified output accuracy' }
|
|
342
|
+
* );
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
function recordHumanReview(reviewedEventId, reviewerId, reviewType, decision, options = {}) {
|
|
346
|
+
const review = {
|
|
347
|
+
reviewedEventId,
|
|
348
|
+
reviewerId,
|
|
349
|
+
reviewType,
|
|
350
|
+
decision,
|
|
351
|
+
modifications: options.modifications,
|
|
352
|
+
reviewDurationMs: options.reviewDurationMs,
|
|
353
|
+
reviewNotes: options.notes,
|
|
354
|
+
complianceFlags: options.complianceFlags || [],
|
|
355
|
+
timestamp: new Date().toISOString(),
|
|
356
|
+
};
|
|
357
|
+
reviewStore.addCompletedReview(review);
|
|
358
|
+
reviewStore.removePendingReview(reviewedEventId);
|
|
359
|
+
return review;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Record a human override of an AI output.
|
|
363
|
+
*
|
|
364
|
+
* @param overriddenEventId - ID of the event being overridden.
|
|
365
|
+
* @param overrideReason - Reason for the override.
|
|
366
|
+
* @param options - Additional options.
|
|
367
|
+
* @returns The created HumanOverride.
|
|
368
|
+
*
|
|
369
|
+
* @example
|
|
370
|
+
* ```typescript
|
|
371
|
+
* recordHumanOverride(
|
|
372
|
+
* 'evt_123',
|
|
373
|
+
* 'Incorrect recommendation',
|
|
374
|
+
* { overrideAuthority: 'supervisor' }
|
|
375
|
+
* );
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
378
|
+
function recordHumanOverride(overriddenEventId, overrideReason, options = {}) {
|
|
379
|
+
let originalHash;
|
|
380
|
+
let correctedHash;
|
|
381
|
+
if (options.originalOutput) {
|
|
382
|
+
const hash = crypto.createHash('sha256').update(options.originalOutput).digest('hex');
|
|
383
|
+
originalHash = `sha256:${hash}`;
|
|
384
|
+
}
|
|
385
|
+
if (options.correctedOutput) {
|
|
386
|
+
const hash = crypto.createHash('sha256').update(options.correctedOutput).digest('hex');
|
|
387
|
+
correctedHash = `sha256:${hash}`;
|
|
388
|
+
}
|
|
389
|
+
const override = {
|
|
390
|
+
overriddenEventId,
|
|
391
|
+
overrideReason,
|
|
392
|
+
originalOutputHash: originalHash,
|
|
393
|
+
correctedOutputHash: correctedHash,
|
|
394
|
+
overrideAuthority: options.overrideAuthority,
|
|
395
|
+
timestamp: new Date().toISOString(),
|
|
396
|
+
};
|
|
397
|
+
reviewStore.addOverride(override);
|
|
398
|
+
return override;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Mark an event as requiring human review.
|
|
402
|
+
*
|
|
403
|
+
* @param eventId - ID of the event requiring review.
|
|
404
|
+
* @param options - Additional options.
|
|
405
|
+
* @returns The created PendingReview.
|
|
406
|
+
*/
|
|
407
|
+
function requireReview(eventId, options = {}) {
|
|
408
|
+
const config = options.config || {};
|
|
409
|
+
const oversightConfig = config.human_oversight || {};
|
|
410
|
+
const timeoutHours = oversightConfig.review_timeout_hours ?? 24;
|
|
411
|
+
const now = Date.now();
|
|
412
|
+
const pending = {
|
|
413
|
+
eventId,
|
|
414
|
+
traceId: options.traceId,
|
|
415
|
+
eventType: options.eventType || 'llm_call',
|
|
416
|
+
model: options.model,
|
|
417
|
+
riskLevel: options.riskLevel,
|
|
418
|
+
createdAt: now,
|
|
419
|
+
timeoutAt: now + timeoutHours * 3600 * 1000,
|
|
420
|
+
};
|
|
421
|
+
reviewStore.addPendingReview(pending);
|
|
422
|
+
return pending;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Get all events pending human review.
|
|
426
|
+
*/
|
|
427
|
+
function getPendingReviews() {
|
|
428
|
+
return reviewStore.listPendingReviews();
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Get reviews that have timed out.
|
|
432
|
+
*/
|
|
433
|
+
function getTimedOutReviews() {
|
|
434
|
+
return reviewStore.listTimedOutReviews(Date.now());
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Check if an event requires human review based on config.
|
|
438
|
+
*
|
|
439
|
+
* @param eventType - Type of event.
|
|
440
|
+
* @param model - Model used.
|
|
441
|
+
* @param riskLevel - Risk level of the event.
|
|
442
|
+
* @param config - Optional config.
|
|
443
|
+
* @returns True if review is required.
|
|
444
|
+
*/
|
|
445
|
+
function checkReviewRequired(eventType, model, riskLevel, config) {
|
|
446
|
+
const oversightConfig = config?.human_oversight;
|
|
447
|
+
if (!oversightConfig?.enabled) {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
const requireFor = oversightConfig.require_review_for || [];
|
|
451
|
+
if (requireFor.length === 0) {
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
for (const condition of requireFor) {
|
|
455
|
+
if (condition.risk_level && riskLevel !== condition.risk_level) {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (condition.event_type && eventType !== condition.event_type) {
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (condition.tool_name && model !== condition.tool_name) {
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Get summary of human oversight for reporting.
|
|
470
|
+
*
|
|
471
|
+
* @returns Summary dict for compliance reports.
|
|
472
|
+
*/
|
|
473
|
+
function getOversightSummary() {
|
|
474
|
+
const completed = reviewStore.listCompletedReviews();
|
|
475
|
+
const pending = reviewStore.listPendingReviews();
|
|
476
|
+
const overrides = reviewStore.listOverrides();
|
|
477
|
+
const timedOut = reviewStore.listTimedOutReviews(Date.now());
|
|
478
|
+
const reviewTimes = completed
|
|
479
|
+
.filter((r) => r.reviewDurationMs !== undefined)
|
|
480
|
+
.map((r) => r.reviewDurationMs);
|
|
481
|
+
const avgReviewTime = reviewTimes.length > 0
|
|
482
|
+
? reviewTimes.reduce((a, b) => a + b, 0) / reviewTimes.length
|
|
483
|
+
: null;
|
|
484
|
+
return {
|
|
485
|
+
reviews_completed: completed.length,
|
|
486
|
+
reviews_pending: pending.length,
|
|
487
|
+
overrides: overrides.length,
|
|
488
|
+
average_review_time_ms: avgReviewTime,
|
|
489
|
+
timed_out_reviews: timedOut.length,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Clear all oversight tracking data.
|
|
494
|
+
*/
|
|
495
|
+
function clearOversightData() {
|
|
496
|
+
reviewStore.clear();
|
|
497
|
+
}
|
package/dist/presets.js
CHANGED
|
@@ -63,7 +63,7 @@ exports.DEVELOPMENT = {
|
|
|
63
63
|
telemetry: {
|
|
64
64
|
enabled: false,
|
|
65
65
|
send_data: false,
|
|
66
|
-
data_residency:
|
|
66
|
+
data_residency: null,
|
|
67
67
|
},
|
|
68
68
|
},
|
|
69
69
|
},
|
|
@@ -110,7 +110,7 @@ exports.POC = {
|
|
|
110
110
|
telemetry: {
|
|
111
111
|
enabled: false,
|
|
112
112
|
send_data: false,
|
|
113
|
-
data_residency:
|
|
113
|
+
data_residency: null,
|
|
114
114
|
},
|
|
115
115
|
},
|
|
116
116
|
},
|
|
@@ -161,7 +161,7 @@ exports.PRODUCTION = {
|
|
|
161
161
|
environment: 'production',
|
|
162
162
|
data_classification: 'confidential',
|
|
163
163
|
},
|
|
164
|
-
sinks: [{ type: 'file', path: './monora_events.jsonl', rotation: 'daily' }],
|
|
164
|
+
sinks: [{ type: 'file', path: './monora_events.jsonl', rotation: 'daily', symlink: true }],
|
|
165
165
|
immutability: {
|
|
166
166
|
enabled: true,
|
|
167
167
|
scope: 'per_trace',
|
|
@@ -202,7 +202,7 @@ exports.STRICT_ENTERPRISE = {
|
|
|
202
202
|
environment: 'production',
|
|
203
203
|
data_classification: 'restricted',
|
|
204
204
|
},
|
|
205
|
-
sinks: [{ type: 'file', path: './monora_events.jsonl', rotation: 'daily' }],
|
|
205
|
+
sinks: [{ type: 'file', path: './monora_events.jsonl', rotation: 'daily', symlink: true }],
|
|
206
206
|
immutability: {
|
|
207
207
|
enabled: true,
|
|
208
208
|
scope: 'per_trace',
|
|
@@ -328,7 +328,7 @@ exports.AUDIT_FIRST = {
|
|
|
328
328
|
environment: 'production',
|
|
329
329
|
data_classification: 'restricted',
|
|
330
330
|
},
|
|
331
|
-
sinks: [{ type: 'file', path: './monora_events.jsonl', rotation: 'daily' }],
|
|
331
|
+
sinks: [{ type: 'file', path: './monora_events.jsonl', rotation: 'daily', symlink: true }],
|
|
332
332
|
immutability: {
|
|
333
333
|
enabled: true,
|
|
334
334
|
scope: 'per_trace',
|
|
@@ -363,7 +363,7 @@ exports.AUDIT_FIRST = {
|
|
|
363
363
|
telemetry: {
|
|
364
364
|
enabled: false,
|
|
365
365
|
send_data: false,
|
|
366
|
-
data_residency:
|
|
366
|
+
data_residency: null,
|
|
367
367
|
},
|
|
368
368
|
},
|
|
369
369
|
},
|
|
@@ -506,7 +506,7 @@ function deepMerge(base, updates) {
|
|
|
506
506
|
deepMerge(base[key], updates[key]);
|
|
507
507
|
}
|
|
508
508
|
else {
|
|
509
|
-
base[key] = updates[key];
|
|
509
|
+
base[key] = deepClone(updates[key]);
|
|
510
510
|
}
|
|
511
511
|
}
|
|
512
512
|
}
|