@elsium-ai/observe 0.9.1 → 0.11.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/dist/audit-sink-jsonl.d.ts +7 -0
- package/dist/audit-sink-jsonl.d.ts.map +1 -0
- package/dist/compliance.d.ts +60 -0
- package/dist/compliance.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +407 -9
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-sink-jsonl.d.ts","sourceRoot":"","sources":["../src/audit-sink-jsonl.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAI7C,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,OAAO,CAAA;CACf;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAwDlE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { AuditEvent, AuditTrail } from './audit';
|
|
2
|
+
export type ComplianceFramework = 'eu-ai-act' | 'colorado-ai-act' | 'owasp-agentic' | 'custom';
|
|
3
|
+
export interface ComplianceReportConfig {
|
|
4
|
+
framework: ComplianceFramework;
|
|
5
|
+
systemName: string;
|
|
6
|
+
systemVersion: string;
|
|
7
|
+
reportPeriod: {
|
|
8
|
+
from: number;
|
|
9
|
+
to: number;
|
|
10
|
+
};
|
|
11
|
+
riskLevel?: 'minimal' | 'limited' | 'high' | 'unacceptable';
|
|
12
|
+
customChecks?: ComplianceCheck[];
|
|
13
|
+
}
|
|
14
|
+
export interface ComplianceCheck {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
category: string;
|
|
19
|
+
evaluate: (events: AuditEvent[]) => ComplianceCheckResult;
|
|
20
|
+
}
|
|
21
|
+
export interface ComplianceCheckResult {
|
|
22
|
+
status: 'pass' | 'fail' | 'warning' | 'not-applicable';
|
|
23
|
+
details: string;
|
|
24
|
+
evidence?: string[];
|
|
25
|
+
recommendations?: string[];
|
|
26
|
+
}
|
|
27
|
+
export interface ComplianceReport {
|
|
28
|
+
id: string;
|
|
29
|
+
framework: ComplianceFramework;
|
|
30
|
+
systemName: string;
|
|
31
|
+
systemVersion: string;
|
|
32
|
+
generatedAt: number;
|
|
33
|
+
reportPeriod: {
|
|
34
|
+
from: number;
|
|
35
|
+
to: number;
|
|
36
|
+
};
|
|
37
|
+
summary: ComplianceSummary;
|
|
38
|
+
checks: ComplianceReportEntry[];
|
|
39
|
+
auditIntegrity: {
|
|
40
|
+
valid: boolean;
|
|
41
|
+
totalEvents: number;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export interface ComplianceSummary {
|
|
45
|
+
totalChecks: number;
|
|
46
|
+
passed: number;
|
|
47
|
+
failed: number;
|
|
48
|
+
warnings: number;
|
|
49
|
+
notApplicable: number;
|
|
50
|
+
overallStatus: 'compliant' | 'non-compliant' | 'needs-review';
|
|
51
|
+
}
|
|
52
|
+
export interface ComplianceReportEntry {
|
|
53
|
+
id: string;
|
|
54
|
+
name: string;
|
|
55
|
+
category: string;
|
|
56
|
+
result: ComplianceCheckResult;
|
|
57
|
+
}
|
|
58
|
+
export declare function generateComplianceReport(auditTrail: AuditTrail, config: ComplianceReportConfig): Promise<ComplianceReport>;
|
|
59
|
+
export declare function formatComplianceReport(report: ComplianceReport): string;
|
|
60
|
+
//# sourceMappingURL=compliance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compliance.d.ts","sourceRoot":"","sources":["../src/compliance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAErD,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,iBAAiB,GAAG,eAAe,GAAG,QAAQ,CAAA;AAE9F,MAAM,WAAW,sBAAsB;IACtC,SAAS,EAAE,mBAAmB,CAAA;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1C,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,cAAc,CAAA;IAC3D,YAAY,CAAC,EAAE,eAAe,EAAE,CAAA;CAChC;AAED,MAAM,WAAW,eAAe;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,qBAAqB,CAAA;CACzD;AAED,MAAM,WAAW,qBAAqB;IACrC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAAA;IACtD,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,mBAAmB,CAAA;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1C,OAAO,EAAE,iBAAiB,CAAA;IAC1B,MAAM,EAAE,qBAAqB,EAAE,CAAA;IAC/B,cAAc,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;CACvD;AAED,MAAM,WAAW,iBAAiB;IACjC,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,WAAW,GAAG,eAAe,GAAG,cAAc,CAAA;CAC7D;AAED,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,qBAAqB,CAAA;CAC7B;AAqQD,wBAAsB,wBAAwB,CAC7C,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,sBAAsB,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CA2C3B;AA2BD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAsCvE"}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ export { createSplunkSink } from './audit-sink-splunk';
|
|
|
16
16
|
export type { SplunkSinkConfig } from './audit-sink-splunk';
|
|
17
17
|
export { createDatadogSink } from './audit-sink-datadog';
|
|
18
18
|
export type { DatadogSinkConfig } from './audit-sink-datadog';
|
|
19
|
+
export { createJsonlSink } from './audit-sink-jsonl';
|
|
20
|
+
export type { JsonlSinkConfig } from './audit-sink-jsonl';
|
|
19
21
|
export { createProvenanceTracker } from './provenance';
|
|
20
22
|
export type { ProvenanceRecord, ProvenanceTracker } from './provenance';
|
|
21
23
|
export { createExperiment, createFileExperimentStore } from './experiment';
|
|
@@ -24,6 +26,8 @@ export { instrumentComplete, instrumentAgent } from './instrument';
|
|
|
24
26
|
export type { InstrumentableAgent } from './instrument';
|
|
25
27
|
export { createStudioExporter } from './studio-exporter';
|
|
26
28
|
export type { StudioExporter, StudioExporterConfig } from './studio-exporter';
|
|
29
|
+
export { generateComplianceReport, formatComplianceReport } from './compliance';
|
|
30
|
+
export type { ComplianceFramework, ComplianceReportConfig, ComplianceCheck, ComplianceCheckResult, ComplianceReport, ComplianceSummary, ComplianceReportEntry, } from './compliance';
|
|
27
31
|
export { toOTelSpan, toOTelExportRequest, toTraceparent, parseTraceparent, injectTraceContext, extractTraceContext, createOTLPExporter, } from './otel';
|
|
28
32
|
export type { OTelSpan, OTelSpanKind, OTelStatusCode, OTelAttribute, OTelAttributeValue, OTelEvent, OTelResource, OTelExportRequest, TraceContext, OTLPExporterConfig, } from './otel';
|
|
29
33
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACnC,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAG1F,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACnE,YAAY,EACX,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACnB,SAAS,EACT,aAAa,EACb,sBAAsB,EACtB,eAAe,EACf,cAAc,GACd,MAAM,eAAe,CAAA;AAGtB,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAClC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAG9F,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AACzC,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAG9D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AAClF,YAAY,EACX,cAAc,EACd,UAAU,EACV,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,GACV,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,YAAY,EAAE,SAAS,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AACnG,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AACxD,YAAY,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AACtD,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AACxD,YAAY,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1282,6 +1282,68 @@ function createDatadogSink(config) {
|
|
|
1282
1282
|
}
|
|
1283
1283
|
};
|
|
1284
1284
|
}
|
|
1285
|
+
// src/audit-sink-jsonl.ts
|
|
1286
|
+
import { mkdir, open } from "node:fs/promises";
|
|
1287
|
+
import { dirname } from "node:path";
|
|
1288
|
+
var log5 = createLogger();
|
|
1289
|
+
function createJsonlSink(config) {
|
|
1290
|
+
const { path, fsync = true } = config;
|
|
1291
|
+
let handle = null;
|
|
1292
|
+
let initPromise = null;
|
|
1293
|
+
let writeChain = Promise.resolve();
|
|
1294
|
+
async function getHandle() {
|
|
1295
|
+
if (handle)
|
|
1296
|
+
return handle;
|
|
1297
|
+
if (initPromise)
|
|
1298
|
+
return initPromise;
|
|
1299
|
+
initPromise = (async () => {
|
|
1300
|
+
await mkdir(dirname(path), { recursive: true });
|
|
1301
|
+
handle = await open(path, "a");
|
|
1302
|
+
return handle;
|
|
1303
|
+
})();
|
|
1304
|
+
return initPromise;
|
|
1305
|
+
}
|
|
1306
|
+
function withWriteLock(fn) {
|
|
1307
|
+
const previous = writeChain;
|
|
1308
|
+
const next = previous.catch(() => {}).then(fn);
|
|
1309
|
+
writeChain = next;
|
|
1310
|
+
return next;
|
|
1311
|
+
}
|
|
1312
|
+
return {
|
|
1313
|
+
name: "jsonl",
|
|
1314
|
+
async send(events) {
|
|
1315
|
+
if (events.length === 0)
|
|
1316
|
+
return;
|
|
1317
|
+
return withWriteLock(async () => {
|
|
1318
|
+
const fh = await getHandle();
|
|
1319
|
+
const payload = `${events.map((e) => JSON.stringify(e)).join(`
|
|
1320
|
+
`)}
|
|
1321
|
+
`;
|
|
1322
|
+
await fh.appendFile(payload, "utf8");
|
|
1323
|
+
if (fsync) {
|
|
1324
|
+
try {
|
|
1325
|
+
await fh.sync();
|
|
1326
|
+
} catch (err2) {
|
|
1327
|
+
log5.warn("jsonl sink fsync failed", { error: err2 });
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
});
|
|
1331
|
+
},
|
|
1332
|
+
async shutdown() {
|
|
1333
|
+
await withWriteLock(async () => {
|
|
1334
|
+
if (handle) {
|
|
1335
|
+
try {
|
|
1336
|
+
if (fsync)
|
|
1337
|
+
await handle.sync();
|
|
1338
|
+
} finally {
|
|
1339
|
+
await handle.close();
|
|
1340
|
+
handle = null;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1285
1347
|
// src/provenance.ts
|
|
1286
1348
|
import { createHash as createHash2 } from "node:crypto";
|
|
1287
1349
|
function sha256(input) {
|
|
@@ -1343,7 +1405,7 @@ function createProvenanceTracker(options) {
|
|
|
1343
1405
|
import { createHash as createHash3 } from "node:crypto";
|
|
1344
1406
|
import { existsSync, mkdirSync, readFileSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1345
1407
|
import { join } from "node:path";
|
|
1346
|
-
var
|
|
1408
|
+
var log6 = createLogger();
|
|
1347
1409
|
function createFileExperimentStore(dir) {
|
|
1348
1410
|
return {
|
|
1349
1411
|
save(name, data) {
|
|
@@ -1354,7 +1416,7 @@ function createFileExperimentStore(dir) {
|
|
|
1354
1416
|
const filePath = join(dir, `${name}.json`);
|
|
1355
1417
|
writeFileSync2(filePath, JSON.stringify(data, null, 2));
|
|
1356
1418
|
} catch (err2) {
|
|
1357
|
-
|
|
1419
|
+
log6.error("Failed to save experiment", {
|
|
1358
1420
|
name,
|
|
1359
1421
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
1360
1422
|
});
|
|
@@ -1385,7 +1447,7 @@ function loadFromStore(store, name, stats) {
|
|
|
1385
1447
|
stats[vName].metrics[key] = { sum: m.sum, count: m.count };
|
|
1386
1448
|
}
|
|
1387
1449
|
}
|
|
1388
|
-
|
|
1450
|
+
log6.debug("Loaded experiment state", { name, totalAssignments: saved.totalAssignments });
|
|
1389
1451
|
}
|
|
1390
1452
|
function recordMetrics(s, metrics) {
|
|
1391
1453
|
for (const [key, value] of Object.entries(metrics)) {
|
|
@@ -1453,7 +1515,7 @@ function createExperiment(config) {
|
|
|
1453
1515
|
const s = stats[variant.name];
|
|
1454
1516
|
if (s)
|
|
1455
1517
|
s.assignments++;
|
|
1456
|
-
|
|
1518
|
+
log6.debug("Experiment assignment", {
|
|
1457
1519
|
experiment: name,
|
|
1458
1520
|
variant: variant.name,
|
|
1459
1521
|
userId
|
|
@@ -1524,7 +1586,7 @@ function instrumentAgent(agent, tracer) {
|
|
|
1524
1586
|
// src/studio-exporter.ts
|
|
1525
1587
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1526
1588
|
import { join as join2 } from "node:path";
|
|
1527
|
-
var
|
|
1589
|
+
var log7 = createLogger();
|
|
1528
1590
|
function ensureDir(dirPath) {
|
|
1529
1591
|
if (!existsSync2(dirPath)) {
|
|
1530
1592
|
mkdirSync2(dirPath, { recursive: true });
|
|
@@ -1534,7 +1596,7 @@ function safeWriteJSON(filePath, data) {
|
|
|
1534
1596
|
try {
|
|
1535
1597
|
writeFileSync3(filePath, JSON.stringify(data, null, 2));
|
|
1536
1598
|
} catch (err2) {
|
|
1537
|
-
|
|
1599
|
+
log7.error("Studio exporter write failed", {
|
|
1538
1600
|
file: filePath,
|
|
1539
1601
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
1540
1602
|
});
|
|
@@ -1585,8 +1647,341 @@ function createStudioExporter(config) {
|
|
|
1585
1647
|
}
|
|
1586
1648
|
};
|
|
1587
1649
|
}
|
|
1650
|
+
// src/compliance.ts
|
|
1651
|
+
function createOWASPAgenticChecks() {
|
|
1652
|
+
return [
|
|
1653
|
+
{
|
|
1654
|
+
id: "owasp-ag-01",
|
|
1655
|
+
name: "Prompt Injection Detection",
|
|
1656
|
+
description: "Verify security violation events exist for injection attempts",
|
|
1657
|
+
category: "Goal Hijacking",
|
|
1658
|
+
evaluate: (events) => {
|
|
1659
|
+
const violations = events.filter((e) => e.type === "security_violation" && e.data.category === "injection");
|
|
1660
|
+
return {
|
|
1661
|
+
status: "pass",
|
|
1662
|
+
details: `${violations.length} injection attempts detected and blocked`,
|
|
1663
|
+
evidence: violations.slice(0, 5).map((v) => `Event ${v.id} at ${new Date(v.timestamp).toISOString()}`)
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
},
|
|
1667
|
+
{
|
|
1668
|
+
id: "owasp-ag-02",
|
|
1669
|
+
name: "Tool Execution Audit",
|
|
1670
|
+
description: "All tool executions are logged in audit trail",
|
|
1671
|
+
category: "Tool Abuse",
|
|
1672
|
+
evaluate: (events) => {
|
|
1673
|
+
const toolEvents = events.filter((e) => e.type === "tool_execution");
|
|
1674
|
+
if (toolEvents.length === 0) {
|
|
1675
|
+
return {
|
|
1676
|
+
status: "warning",
|
|
1677
|
+
details: "No tool execution events found in audit trail",
|
|
1678
|
+
recommendations: ["Enable audit middleware for tool executions"]
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
return {
|
|
1682
|
+
status: "pass",
|
|
1683
|
+
details: `${toolEvents.length} tool executions audited`
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
id: "owasp-ag-03",
|
|
1689
|
+
name: "Budget Enforcement",
|
|
1690
|
+
description: "Budget alerts and limits are enforced",
|
|
1691
|
+
category: "Runaway Agents",
|
|
1692
|
+
evaluate: (events) => {
|
|
1693
|
+
const budgetAlerts = events.filter((e) => e.type === "budget_alert");
|
|
1694
|
+
const policyViolations = events.filter((e) => e.type === "policy_violation" && (e.data.policyName === "cost-limit" || e.data.policyName === "token-limit"));
|
|
1695
|
+
return {
|
|
1696
|
+
status: "pass",
|
|
1697
|
+
details: `${budgetAlerts.length} budget alerts, ${policyViolations.length} policy violations enforced`
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
},
|
|
1701
|
+
{
|
|
1702
|
+
id: "owasp-ag-04",
|
|
1703
|
+
name: "Audit Trail Integrity",
|
|
1704
|
+
description: "Audit trail hash chain is intact",
|
|
1705
|
+
category: "Audit Integrity",
|
|
1706
|
+
evaluate: () => ({
|
|
1707
|
+
status: "pass",
|
|
1708
|
+
details: "Audit trail integrity verified separately via verifyIntegrity()"
|
|
1709
|
+
})
|
|
1710
|
+
},
|
|
1711
|
+
{
|
|
1712
|
+
id: "owasp-ag-05",
|
|
1713
|
+
name: "Secret Redaction Active",
|
|
1714
|
+
description: "Security middleware redacts secrets from inputs and outputs",
|
|
1715
|
+
category: "Data Exfiltration",
|
|
1716
|
+
evaluate: (events) => {
|
|
1717
|
+
const redactions = events.filter((e) => e.type === "security_violation" && (e.data.category === "secret_detected" || e.data.redacted === true));
|
|
1718
|
+
return {
|
|
1719
|
+
status: "pass",
|
|
1720
|
+
details: `${redactions.length} secret redaction events recorded`
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1723
|
+
},
|
|
1724
|
+
{
|
|
1725
|
+
id: "owasp-ag-06",
|
|
1726
|
+
name: "Approval Gates Active",
|
|
1727
|
+
description: "High-risk operations require approval",
|
|
1728
|
+
category: "Privilege Escalation",
|
|
1729
|
+
evaluate: (events) => {
|
|
1730
|
+
const approvalRequests = events.filter((e) => e.type === "approval_request");
|
|
1731
|
+
const approvalDecisions = events.filter((e) => e.type === "approval_decision");
|
|
1732
|
+
if (approvalRequests.length === 0) {
|
|
1733
|
+
return {
|
|
1734
|
+
status: "warning",
|
|
1735
|
+
details: "No approval requests found — ensure approval gates are configured",
|
|
1736
|
+
recommendations: ["Configure approval gates for high-risk tool calls"]
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
const denied = approvalDecisions.filter((e) => e.data.approved === false);
|
|
1740
|
+
return {
|
|
1741
|
+
status: "pass",
|
|
1742
|
+
details: `${approvalRequests.length} approval requests, ${denied.length} denied`
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
];
|
|
1747
|
+
}
|
|
1748
|
+
function createEUAIActChecks(riskLevel) {
|
|
1749
|
+
return [
|
|
1750
|
+
{
|
|
1751
|
+
id: "eu-ai-01",
|
|
1752
|
+
name: "Risk Classification",
|
|
1753
|
+
description: "System risk level is documented",
|
|
1754
|
+
category: "Risk Management",
|
|
1755
|
+
evaluate: () => ({
|
|
1756
|
+
status: "pass",
|
|
1757
|
+
details: `System classified as "${riskLevel}" risk`
|
|
1758
|
+
})
|
|
1759
|
+
},
|
|
1760
|
+
{
|
|
1761
|
+
id: "eu-ai-02",
|
|
1762
|
+
name: "Human Oversight",
|
|
1763
|
+
description: "Human-in-the-loop mechanisms are available (approval gates)",
|
|
1764
|
+
category: "Human Oversight",
|
|
1765
|
+
evaluate: (events) => {
|
|
1766
|
+
const approvals = events.filter((e) => e.type === "approval_request" || e.type === "approval_decision");
|
|
1767
|
+
if (riskLevel === "high" && approvals.length === 0) {
|
|
1768
|
+
return {
|
|
1769
|
+
status: "fail",
|
|
1770
|
+
details: "High-risk system requires human oversight mechanisms",
|
|
1771
|
+
recommendations: ["Implement approval gates for critical operations"]
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
return {
|
|
1775
|
+
status: "pass",
|
|
1776
|
+
details: `${approvals.length} human oversight events recorded`
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
},
|
|
1780
|
+
{
|
|
1781
|
+
id: "eu-ai-03",
|
|
1782
|
+
name: "Transparency Logging",
|
|
1783
|
+
description: "All AI decisions are logged with full traceability",
|
|
1784
|
+
category: "Transparency",
|
|
1785
|
+
evaluate: (events) => {
|
|
1786
|
+
const llmCalls = events.filter((e) => e.type === "llm_call");
|
|
1787
|
+
const withTraceId = llmCalls.filter((e) => e.traceId);
|
|
1788
|
+
if (llmCalls.length === 0) {
|
|
1789
|
+
return {
|
|
1790
|
+
status: "fail",
|
|
1791
|
+
details: "No LLM call events logged",
|
|
1792
|
+
recommendations: ["Enable audit middleware on gateway"]
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
const traceRate = withTraceId.length / llmCalls.length;
|
|
1796
|
+
return {
|
|
1797
|
+
status: traceRate >= 0.95 ? "pass" : "warning",
|
|
1798
|
+
details: `${llmCalls.length} LLM calls logged, ${Math.round(traceRate * 100)}% with trace IDs`,
|
|
1799
|
+
recommendations: traceRate < 0.95 ? ["Ensure all requests include trace IDs for full traceability"] : undefined
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
},
|
|
1803
|
+
{
|
|
1804
|
+
id: "eu-ai-04",
|
|
1805
|
+
name: "Data Governance",
|
|
1806
|
+
description: "PII and sensitive data are protected",
|
|
1807
|
+
category: "Data Governance",
|
|
1808
|
+
evaluate: (events) => {
|
|
1809
|
+
const securityEvents = events.filter((e) => e.type === "security_violation");
|
|
1810
|
+
return {
|
|
1811
|
+
status: "pass",
|
|
1812
|
+
details: `${securityEvents.length} security events recorded — data protection active`
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
},
|
|
1816
|
+
{
|
|
1817
|
+
id: "eu-ai-05",
|
|
1818
|
+
name: "Record Keeping",
|
|
1819
|
+
description: "Audit trail is maintained with tamper-evident hash chain",
|
|
1820
|
+
category: "Record Keeping",
|
|
1821
|
+
evaluate: () => ({
|
|
1822
|
+
status: "pass",
|
|
1823
|
+
details: "SHA-256 hash-chained audit trail is active"
|
|
1824
|
+
})
|
|
1825
|
+
}
|
|
1826
|
+
];
|
|
1827
|
+
}
|
|
1828
|
+
function createColoradoAIActChecks() {
|
|
1829
|
+
return [
|
|
1830
|
+
{
|
|
1831
|
+
id: "co-ai-01",
|
|
1832
|
+
name: "Impact Assessment Documentation",
|
|
1833
|
+
description: "AI system impact is documented and assessable",
|
|
1834
|
+
category: "Impact Assessment",
|
|
1835
|
+
evaluate: (events) => {
|
|
1836
|
+
const totalEvents = events.length;
|
|
1837
|
+
return {
|
|
1838
|
+
status: totalEvents > 0 ? "pass" : "warning",
|
|
1839
|
+
details: `${totalEvents} events available for impact assessment`,
|
|
1840
|
+
recommendations: totalEvents === 0 ? ["Enable comprehensive audit logging for impact assessment evidence"] : undefined
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
},
|
|
1844
|
+
{
|
|
1845
|
+
id: "co-ai-02",
|
|
1846
|
+
name: "Algorithmic Discrimination Prevention",
|
|
1847
|
+
description: "Content policy and guardrails prevent discriminatory outputs",
|
|
1848
|
+
category: "Fairness",
|
|
1849
|
+
evaluate: (events) => {
|
|
1850
|
+
const policyViolations = events.filter((e) => e.type === "policy_violation" && e.data.policyName === "content-policy");
|
|
1851
|
+
return {
|
|
1852
|
+
status: "pass",
|
|
1853
|
+
details: `Content policy active — ${policyViolations.length} violations blocked`
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
},
|
|
1857
|
+
{
|
|
1858
|
+
id: "co-ai-03",
|
|
1859
|
+
name: "Consumer Notification Capability",
|
|
1860
|
+
description: "System can notify users when AI is making consequential decisions",
|
|
1861
|
+
category: "Transparency",
|
|
1862
|
+
evaluate: (events) => {
|
|
1863
|
+
const auditCount = events.filter((e) => e.type === "llm_call").length;
|
|
1864
|
+
return {
|
|
1865
|
+
status: "pass",
|
|
1866
|
+
details: `${auditCount} AI decisions logged — notification capability supported via audit trail`
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
];
|
|
1871
|
+
}
|
|
1872
|
+
function getChecksForFramework(config) {
|
|
1873
|
+
switch (config.framework) {
|
|
1874
|
+
case "owasp-agentic":
|
|
1875
|
+
return createOWASPAgenticChecks();
|
|
1876
|
+
case "eu-ai-act":
|
|
1877
|
+
return createEUAIActChecks(config.riskLevel ?? "limited");
|
|
1878
|
+
case "colorado-ai-act":
|
|
1879
|
+
return createColoradoAIActChecks();
|
|
1880
|
+
case "custom":
|
|
1881
|
+
return config.customChecks ?? [];
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
async function generateComplianceReport(auditTrail, config) {
|
|
1885
|
+
const events = await auditTrail.query({
|
|
1886
|
+
fromTimestamp: config.reportPeriod.from,
|
|
1887
|
+
toTimestamp: config.reportPeriod.to
|
|
1888
|
+
});
|
|
1889
|
+
const integrity = await auditTrail.verifyIntegrity();
|
|
1890
|
+
const checks = getChecksForFramework(config);
|
|
1891
|
+
const entries = checks.map((check) => ({
|
|
1892
|
+
id: check.id,
|
|
1893
|
+
name: check.name,
|
|
1894
|
+
category: check.category,
|
|
1895
|
+
result: check.evaluate(events)
|
|
1896
|
+
}));
|
|
1897
|
+
const passed = entries.filter((e) => e.result.status === "pass").length;
|
|
1898
|
+
const failed = entries.filter((e) => e.result.status === "fail").length;
|
|
1899
|
+
const warnings = entries.filter((e) => e.result.status === "warning").length;
|
|
1900
|
+
const notApplicable = entries.filter((e) => e.result.status === "not-applicable").length;
|
|
1901
|
+
let overallStatus = "compliant";
|
|
1902
|
+
if (failed > 0)
|
|
1903
|
+
overallStatus = "non-compliant";
|
|
1904
|
+
else if (warnings > 0)
|
|
1905
|
+
overallStatus = "needs-review";
|
|
1906
|
+
return {
|
|
1907
|
+
id: `compliance_${config.framework}_${Date.now().toString(36)}`,
|
|
1908
|
+
framework: config.framework,
|
|
1909
|
+
systemName: config.systemName,
|
|
1910
|
+
systemVersion: config.systemVersion,
|
|
1911
|
+
generatedAt: Date.now(),
|
|
1912
|
+
reportPeriod: config.reportPeriod,
|
|
1913
|
+
summary: {
|
|
1914
|
+
totalChecks: entries.length,
|
|
1915
|
+
passed,
|
|
1916
|
+
failed,
|
|
1917
|
+
warnings,
|
|
1918
|
+
notApplicable,
|
|
1919
|
+
overallStatus
|
|
1920
|
+
},
|
|
1921
|
+
checks: entries,
|
|
1922
|
+
auditIntegrity: { valid: integrity.valid, totalEvents: integrity.totalEvents }
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
var STATUS_ICONS = {
|
|
1926
|
+
pass: "[PASS]",
|
|
1927
|
+
fail: "[FAIL]",
|
|
1928
|
+
warning: "[WARN]",
|
|
1929
|
+
"not-applicable": "[N/A]"
|
|
1930
|
+
};
|
|
1931
|
+
function formatCheckEntry(check) {
|
|
1932
|
+
const icon = STATUS_ICONS[check.result.status] ?? "[N/A]";
|
|
1933
|
+
const lines = [`**${icon} ${check.id}: ${check.name}**`, "", check.result.details];
|
|
1934
|
+
if (check.result.evidence?.length) {
|
|
1935
|
+
lines.push("", "Evidence:");
|
|
1936
|
+
lines.push(...check.result.evidence.map((e) => `- ${e}`));
|
|
1937
|
+
}
|
|
1938
|
+
if (check.result.recommendations?.length) {
|
|
1939
|
+
lines.push("", "Recommendations:");
|
|
1940
|
+
lines.push(...check.result.recommendations.map((r) => `- ${r}`));
|
|
1941
|
+
}
|
|
1942
|
+
lines.push("");
|
|
1943
|
+
return lines;
|
|
1944
|
+
}
|
|
1945
|
+
function formatComplianceReport(report) {
|
|
1946
|
+
const lines = [
|
|
1947
|
+
`# Compliance Report: ${report.framework.toUpperCase()}`,
|
|
1948
|
+
"",
|
|
1949
|
+
`**System:** ${report.systemName} v${report.systemVersion}`,
|
|
1950
|
+
`**Generated:** ${new Date(report.generatedAt).toISOString()}`,
|
|
1951
|
+
`**Period:** ${new Date(report.reportPeriod.from).toISOString()} to ${new Date(report.reportPeriod.to).toISOString()}`,
|
|
1952
|
+
`**Status:** ${report.summary.overallStatus.toUpperCase()}`,
|
|
1953
|
+
"",
|
|
1954
|
+
"## Summary",
|
|
1955
|
+
"",
|
|
1956
|
+
"| Metric | Count |",
|
|
1957
|
+
"|--------|-------|",
|
|
1958
|
+
`| Total Checks | ${report.summary.totalChecks} |`,
|
|
1959
|
+
`| Passed | ${report.summary.passed} |`,
|
|
1960
|
+
`| Failed | ${report.summary.failed} |`,
|
|
1961
|
+
`| Warnings | ${report.summary.warnings} |`,
|
|
1962
|
+
`| N/A | ${report.summary.notApplicable} |`,
|
|
1963
|
+
"",
|
|
1964
|
+
"## Audit Trail Integrity",
|
|
1965
|
+
"",
|
|
1966
|
+
`- Valid: ${report.auditIntegrity.valid ? "Yes" : "NO"}`,
|
|
1967
|
+
`- Total Events: ${report.auditIntegrity.totalEvents}`,
|
|
1968
|
+
"",
|
|
1969
|
+
"## Checks",
|
|
1970
|
+
""
|
|
1971
|
+
];
|
|
1972
|
+
const categories = [...new Set(report.checks.map((c) => c.category))];
|
|
1973
|
+
for (const category of categories) {
|
|
1974
|
+
lines.push(`### ${category}`, "");
|
|
1975
|
+
const categoryChecks = report.checks.filter((c) => c.category === category);
|
|
1976
|
+
for (const check of categoryChecks) {
|
|
1977
|
+
lines.push(...formatCheckEntry(check));
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
return lines.join(`
|
|
1981
|
+
`);
|
|
1982
|
+
}
|
|
1588
1983
|
// src/otel.ts
|
|
1589
|
-
var
|
|
1984
|
+
var log8 = createLogger();
|
|
1590
1985
|
var SPAN_KIND_MAP = {
|
|
1591
1986
|
llm: 3,
|
|
1592
1987
|
tool: 1,
|
|
@@ -1727,10 +2122,10 @@ function createOTLPExporter(config) {
|
|
|
1727
2122
|
body: JSON.stringify(payload)
|
|
1728
2123
|
});
|
|
1729
2124
|
if (!response.ok) {
|
|
1730
|
-
|
|
2125
|
+
log8.error(`OTLP export failed: ${response.status} ${response.statusText}`);
|
|
1731
2126
|
}
|
|
1732
2127
|
} catch (err2) {
|
|
1733
|
-
|
|
2128
|
+
log8.error("OTLP export error", { error: err2 instanceof Error ? err2.message : String(err2) });
|
|
1734
2129
|
}
|
|
1735
2130
|
}
|
|
1736
2131
|
function startAutoFlush() {
|
|
@@ -1776,6 +2171,8 @@ export {
|
|
|
1776
2171
|
instrumentComplete,
|
|
1777
2172
|
instrumentAgent,
|
|
1778
2173
|
injectTraceContext,
|
|
2174
|
+
generateComplianceReport,
|
|
2175
|
+
formatComplianceReport,
|
|
1779
2176
|
extractTraceContext,
|
|
1780
2177
|
createWebhookSink,
|
|
1781
2178
|
createStudioExporter,
|
|
@@ -1785,6 +2182,7 @@ export {
|
|
|
1785
2182
|
createProvenanceTracker,
|
|
1786
2183
|
createOTLPExporter,
|
|
1787
2184
|
createMetrics,
|
|
2185
|
+
createJsonlSink,
|
|
1788
2186
|
createFileExperimentStore,
|
|
1789
2187
|
createExperiment,
|
|
1790
2188
|
createDatadogSink,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elsium-ai/observe",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Observability, tracing, and cost tracking for ElsiumAI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Eric Utrera <ebutrera9103@gmail.com>",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dev": "bun --watch src/index.ts"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@elsium-ai/core": "^0.
|
|
29
|
+
"@elsium-ai/core": "^0.11.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"typescript": "^5.7.0"
|