@blamejs/core 0.9.24 → 0.9.38

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/index.js CHANGED
@@ -87,6 +87,7 @@ var storage = require("./lib/storage");
87
87
  var safeJson = require("./lib/safe-json");
88
88
  var safeJsonPath = require("./lib/safe-jsonpath");
89
89
  var safeMime = require("./lib/safe-mime");
90
+ var safeDns = require("./lib/safe-dns");
90
91
  var mailStore = require("./lib/mail-store");
91
92
  var ntpCheck = require("./lib/ntp-check");
92
93
  var auditSign = require("./lib/audit-sign");
@@ -160,6 +161,9 @@ var guardHtml = require("./lib/guard-html");
160
161
  var guardSvg = require("./lib/guard-svg");
161
162
  var guardFilename = require("./lib/guard-filename");
162
163
  var guardMessageId = require("./lib/guard-message-id");
164
+ var guardSmtpCommand = require("./lib/guard-smtp-command");
165
+ var guardEnvelope = require("./lib/guard-envelope");
166
+ var guardDsn = require("./lib/guard-dsn");
163
167
  var guardMailQuery = require("./lib/guard-mail-query");
164
168
  var guardMailCompose = require("./lib/guard-mail-compose");
165
169
  var guardMailReply = require("./lib/guard-mail-reply");
@@ -168,9 +172,22 @@ var guardMailSieve = require("./lib/guard-mail-sieve");
168
172
  var guardAgentRegistry = require("./lib/guard-agent-registry");
169
173
  var guardIdempotencyKey = require("./lib/guard-idempotency-key");
170
174
  var guardStreamArgs = require("./lib/guard-stream-args");
175
+ var guardEventBusTopic = require("./lib/guard-event-bus-topic");
176
+ var guardEventBusPayload = require("./lib/guard-event-bus-payload");
177
+ var guardTenantId = require("./lib/guard-tenant-id");
178
+ var guardSagaConfig = require("./lib/guard-saga-config");
179
+ var guardPostureChain = require("./lib/guard-posture-chain");
180
+ var guardTraceContext = require("./lib/guard-trace-context");
181
+ var guardSnapshotEnvelope = require("./lib/guard-snapshot-envelope");
171
182
  var agentOrchestrator = require("./lib/agent-orchestrator");
172
183
  var agentIdempotency = require("./lib/agent-idempotency");
173
184
  var agentStream = require("./lib/agent-stream");
185
+ var agentEventBus = require("./lib/agent-event-bus");
186
+ var agentTenant = require("./lib/agent-tenant");
187
+ var agentSaga = require("./lib/agent-saga");
188
+ var agentPostureChain = require("./lib/agent-posture-chain");
189
+ var agentTrace = require("./lib/agent-trace");
190
+ var agentSnapshot = require("./lib/agent-snapshot");
174
191
  var guardArchive = require("./lib/guard-archive");
175
192
  var guardJson = require("./lib/guard-json");
176
193
  var guardYaml = require("./lib/guard-yaml");
@@ -237,6 +254,9 @@ var csv = require("./lib/csv");
237
254
  var time = require("./lib/time");
238
255
  var uuid = require("./lib/uuid");
239
256
  var mail = require("./lib/mail");
257
+ mail.rbl = require("./lib/mail-rbl");
258
+ mail.greylist = require("./lib/mail-greylist");
259
+ mail.helo = require("./lib/mail-helo");
240
260
  var mailArf = require("./lib/mail-arf");
241
261
  var mailBounce = require("./lib/mail-bounce");
242
262
  var mailMdn = require("./lib/mail-mdn");
@@ -409,6 +429,9 @@ module.exports = {
409
429
  guardSvg: guardSvg,
410
430
  guardFilename: guardFilename,
411
431
  guardMessageId: guardMessageId,
432
+ guardSmtpCommand: guardSmtpCommand,
433
+ guardEnvelope: guardEnvelope,
434
+ guardDsn: guardDsn,
412
435
  guardMailQuery: guardMailQuery,
413
436
  guardMailCompose: guardMailCompose,
414
437
  guardMailReply: guardMailReply,
@@ -417,7 +440,14 @@ module.exports = {
417
440
  guardAgentRegistry: guardAgentRegistry,
418
441
  guardIdempotencyKey: guardIdempotencyKey,
419
442
  guardStreamArgs: guardStreamArgs,
420
- agent: { orchestrator: agentOrchestrator, idempotency: agentIdempotency, stream: agentStream },
443
+ guardEventBusTopic: guardEventBusTopic,
444
+ guardEventBusPayload: guardEventBusPayload,
445
+ guardTenantId: guardTenantId,
446
+ guardSagaConfig: guardSagaConfig,
447
+ guardPostureChain: guardPostureChain,
448
+ guardTraceContext: guardTraceContext,
449
+ guardSnapshotEnvelope: guardSnapshotEnvelope,
450
+ agent: { orchestrator: agentOrchestrator, idempotency: agentIdempotency, stream: agentStream, eventBus: agentEventBus, tenant: agentTenant, saga: agentSaga, postureChain: agentPostureChain, trace: agentTrace, snapshot: agentSnapshot },
421
451
  guardArchive: guardArchive,
422
452
  guardJson: guardJson,
423
453
  guardYaml: guardYaml,
@@ -498,6 +528,7 @@ module.exports = {
498
528
  safeJson: safeJson,
499
529
  safeJsonPath: safeJsonPath,
500
530
  safeMime: safeMime,
531
+ safeDns: safeDns,
501
532
  mailStore: mailStore,
502
533
  safeSchema: safeSchema,
503
534
  pagination: pagination,
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ /**
3
+ * b.agent._audit — internal shared audit-emit helper for the agent
4
+ * substrate (`b.agent.orchestrator` / `b.agent.idempotency` /
5
+ * `b.agent.stream` / `b.agent.eventBus` / future substrate slices).
6
+ *
7
+ * Each agent primitive emits audit events at lifecycle boundaries
8
+ * (registered / opened / closed / replay / denied / drop / etc). The
9
+ * emit logic is identical: actor shape → audit.safeEmit() → swallow
10
+ * any audit-side failures. Extracted here so the 4+ agent substrate
11
+ * modules don't re-implement the same wrapper.
12
+ *
13
+ * Internal — operator-facing surface is each primitive's `.audit`
14
+ * opt; this is the implementation detail.
15
+ */
16
+
17
+ function safeAudit(auditImpl, action, actor, metadata) {
18
+ try {
19
+ auditImpl.safeEmit({
20
+ action: action,
21
+ actor: actor ? { id: actor.id, roles: actor.roles || [] } : { id: "<system>" },
22
+ outcome: _outcomeFor(action),
23
+ metadata: metadata || {},
24
+ });
25
+ } catch (_e) { /* drop-silent — audit failures don't crash the call */ }
26
+ }
27
+
28
+ // "denied" / "drop" / "threw" / "different_args" / "miss" / "not_implemented"
29
+ // all imply failure outcome; anything else is success. Per-primitive
30
+ // classification can override by passing a metadata.outcome — that's
31
+ // merged in by the caller, not here.
32
+ function _outcomeFor(action) {
33
+ if (typeof action !== "string") return "success";
34
+ if (action.indexOf("denied") >= 0) return "failure";
35
+ if (action.indexOf("drop") >= 0) return "failure";
36
+ if (action.indexOf("threw") >= 0) return "failure";
37
+ if (action.indexOf("different_args") >= 0) return "failure";
38
+ if (action.indexOf("miss") >= 0) return "failure";
39
+ if (action.indexOf("not_implemented") >= 0) return "failure";
40
+ return "success";
41
+ }
42
+
43
+ module.exports = {
44
+ safeAudit: safeAudit,
45
+ };
@@ -0,0 +1,336 @@
1
+ "use strict";
2
+ /**
3
+ * @module b.agent.eventBus
4
+ * @nav Agent
5
+ * @title Agent Event Bus
6
+ * @order 65
7
+ *
8
+ * @intro
9
+ * Typed cross-agent publish/subscribe on top of `b.pubsub` (or any
10
+ * pubsub-shaped instance with `publish` / `subscribe` /
11
+ * `unsubscribe`). Substrate for every agent-to-agent reaction the
12
+ * mail stack + future agents need: `mail.scan.malware-detected` →
13
+ * MX refuses source, `mail.crypto.key-rotated` → vault invalidates
14
+ * cached recipient keys, `ai.classify.prompt-injection-detected` →
15
+ * agent quarantines, etc.
16
+ *
17
+ * The bus owns:
18
+ *
19
+ * - **Topic registry** — `registerTopic(name, { schema, posture,
20
+ * permissions, tenantScope })` declares the wire contract at
21
+ * boot. Unknown topics refuse publish + subscribe so typos
22
+ * fail loudly.
23
+ * - **Schema enforcement** — every payload validated against the
24
+ * declared schema before publish AND at each delivery
25
+ * (defends in-flight tampering).
26
+ * - **Permission gating** — `b.permissions.check(actor, scope)`
27
+ * on every publish + subscribe.
28
+ * - **Posture re-validation at delivery** — same shape as
29
+ * v0.9.20 cross-queue posture check.
30
+ * - **Audit lifecycle** — publish / subscribe / delivery / refused
31
+ * events emit to the operator's audit chain.
32
+ *
33
+ * ```js
34
+ * var bus = b.agent.eventBus.create({
35
+ * pubsub: myPubsub,
36
+ * audit: b.audit,
37
+ * permissions: myPerms,
38
+ * });
39
+ *
40
+ * bus.registerTopic("mail.scan.malware-detected", {
41
+ * schema: {
42
+ * source: "string",
43
+ * confidence: "number",
44
+ * detectedAt: "isoDateTime",
45
+ * },
46
+ * posture: "soc2",
47
+ * permissions: {
48
+ * publish: ["mail-scan:write"],
49
+ * subscribe: ["mail-mx:write"],
50
+ * },
51
+ * });
52
+ *
53
+ * await bus.publish("mail.scan.malware-detected", {
54
+ * source: "1.2.3.4", confidence: 0.95, detectedAt: new Date().toISOString(),
55
+ * }, { actor: { id: "scan-agent", roles: ["mail-scan-internal"] } });
56
+ * ```
57
+ *
58
+ * @card
59
+ * Typed cross-agent publish/subscribe. Topics registered with schema
60
+ * + posture + permissions; every payload validated; subscriber-side
61
+ * posture re-validated at delivery so no posture downgrade survives
62
+ * the bus boundary.
63
+ */
64
+
65
+ var lazyRequire = require("./lazy-require");
66
+ var { defineClass } = require("./framework-error");
67
+ var guardEventBusTopic = require("./guard-event-bus-topic");
68
+ var guardEventBusPayload = require("./guard-event-bus-payload");
69
+ var agentAudit = require("./agent-audit");
70
+
71
+ var audit = lazyRequire(function () { return require("./audit"); });
72
+
73
+ var AgentEventBusError = defineClass("AgentEventBusError", { alwaysPermanent: true });
74
+
75
+ /**
76
+ * @primitive b.agent.eventBus.create
77
+ * @signature b.agent.eventBus.create(opts)
78
+ * @since 0.9.25
79
+ * @status stable
80
+ * @related b.agent.orchestrator.create, b.pubsub.create
81
+ *
82
+ * Create the bus facade. Returns an instance with `registerTopic` /
83
+ * `publish` / `subscribe` / `listTopics`. Operator supplies a pubsub-
84
+ * shaped backend; framework owns schema validation, permission
85
+ * gating, posture re-validation, audit lifecycle.
86
+ *
87
+ * @opts
88
+ * pubsub: { publish, subscribe, unsubscribe }, // required
89
+ * audit: b.audit namespace, // optional
90
+ * permissions: b.permissions instance, // optional
91
+ *
92
+ * @example
93
+ * var bus = b.agent.eventBus.create({ pubsub: myPubsub });
94
+ * bus.registerTopic("mail.scan.malware-detected", {
95
+ * schema: { source: "string" },
96
+ * });
97
+ * await bus.publish("mail.scan.malware-detected", { source: "1.2.3.4" });
98
+ */
99
+ function create(opts) {
100
+ if (!opts || typeof opts !== "object") {
101
+ throw new AgentEventBusError("agent-event-bus/bad-opts",
102
+ "create: opts required");
103
+ }
104
+ if (!opts.pubsub || typeof opts.pubsub.publish !== "function" ||
105
+ typeof opts.pubsub.subscribe !== "function") {
106
+ throw new AgentEventBusError("agent-event-bus/bad-pubsub",
107
+ "create: opts.pubsub must expose { publish, subscribe }");
108
+ }
109
+ var auditImpl = opts.audit || audit();
110
+ var permissions = opts.permissions || null;
111
+ var topics = new Map();
112
+
113
+ return {
114
+ registerTopic: function (name, topicOpts) { return _registerTopic(topics, name, topicOpts || {}, auditImpl); },
115
+ publish: function (name, payload, pOpts) { return _publish(topics, opts.pubsub, name, payload, pOpts || {}, permissions, auditImpl); },
116
+ subscribe: function (name, handler, sOpts) { return _subscribe(topics, opts.pubsub, name, handler, sOpts || {}, permissions, auditImpl); },
117
+ listTopics: function (args) { return _listTopics(topics, args || {}, permissions); },
118
+ AgentEventBusError: AgentEventBusError,
119
+ guards: {
120
+ topic: guardEventBusTopic,
121
+ payload: guardEventBusPayload,
122
+ },
123
+ };
124
+ }
125
+
126
+ // ---- Registry -------------------------------------------------------------
127
+
128
+ function _registerTopic(topics, name, topicOpts, auditImpl) {
129
+ guardEventBusTopic.validate(name);
130
+ if (topics.has(name)) {
131
+ throw new AgentEventBusError("agent-event-bus/topic-duplicate",
132
+ "registerTopic: '" + name + "' already registered");
133
+ }
134
+ if (!topicOpts.schema || typeof topicOpts.schema !== "object") {
135
+ throw new AgentEventBusError("agent-event-bus/bad-schema",
136
+ "registerTopic: schema required (flat key→type map)");
137
+ }
138
+ var entry = {
139
+ name: name,
140
+ schema: Object.freeze(Object.assign({}, topicOpts.schema)),
141
+ posture: topicOpts.posture || null,
142
+ tenantScope: topicOpts.tenantScope === true,
143
+ permissions: {
144
+ publish: topicOpts.permissions && Array.isArray(topicOpts.permissions.publish)
145
+ ? topicOpts.permissions.publish.slice() : null,
146
+ subscribe: topicOpts.permissions && Array.isArray(topicOpts.permissions.subscribe)
147
+ ? topicOpts.permissions.subscribe.slice() : null,
148
+ },
149
+ registeredAt: Date.now(),
150
+ };
151
+ topics.set(name, entry);
152
+ _safeAudit(auditImpl, "agent.event_bus.topic_registered", null, {
153
+ name: name, posture: entry.posture, tenantScope: entry.tenantScope,
154
+ });
155
+ }
156
+
157
+ function _listTopics(topics, args, permissions) {
158
+ // Permission gate: list-topics requires no special scope by default;
159
+ // operator can wrap with their own permissions instance for stricter.
160
+ var out = [];
161
+ topics.forEach(function (entry) {
162
+ if (args.kind && entry.kind && entry.kind !== args.kind) return;
163
+ out.push({
164
+ name: entry.name,
165
+ schema: entry.schema,
166
+ posture: entry.posture,
167
+ tenantScope: entry.tenantScope,
168
+ registeredAt: entry.registeredAt,
169
+ });
170
+ });
171
+ return out;
172
+ }
173
+
174
+ // ---- Publish --------------------------------------------------------------
175
+
176
+ async function _publish(topics, pubsub, name, payload, pOpts, permissions, auditImpl) {
177
+ guardEventBusTopic.validate(name);
178
+ var entry = topics.get(name);
179
+ if (!entry) {
180
+ throw new AgentEventBusError("agent-event-bus/unknown-topic",
181
+ "publish: topic '" + name + "' not registered");
182
+ }
183
+ // Permission check for publish.
184
+ if (permissions && entry.permissions.publish) {
185
+ if (!pOpts.actor) {
186
+ throw new AgentEventBusError("agent-event-bus/no-actor",
187
+ "publish: topic '" + name + "' requires actor");
188
+ }
189
+ var allowedPub = false;
190
+ for (var i = 0; i < entry.permissions.publish.length; i += 1) {
191
+ if (permissions.check(pOpts.actor, entry.permissions.publish[i])) {
192
+ allowedPub = true; break;
193
+ }
194
+ }
195
+ if (!allowedPub) {
196
+ _safeAudit(auditImpl, "agent.event_bus.publish_denied", pOpts.actor, { topic: name });
197
+ throw new AgentEventBusError("agent-event-bus/publish-denied",
198
+ "publish: actor lacks any of " + JSON.stringify(entry.permissions.publish) +
199
+ " required for topic '" + name + "'");
200
+ }
201
+ }
202
+ // Schema validation.
203
+ guardEventBusPayload.validate(payload, entry.schema);
204
+ // Wrap the payload with topic metadata so subscribers can see the
205
+ // posture + tenantScope at delivery (re-validation).
206
+ var wrapped = {
207
+ _topic: name,
208
+ _posture: entry.posture,
209
+ _tenantId: pOpts.actor && pOpts.actor.tenantId ? pOpts.actor.tenantId : null,
210
+ _publishedAt: Date.now(),
211
+ payload: payload,
212
+ };
213
+ await pubsub.publish(name, wrapped);
214
+ _safeAudit(auditImpl, "agent.event_bus.published", pOpts.actor, {
215
+ topic: name, posture: entry.posture,
216
+ });
217
+ return { topic: name, publishedAt: wrapped._publishedAt };
218
+ }
219
+
220
+ // ---- Subscribe ------------------------------------------------------------
221
+
222
+ async function _subscribe(topics, pubsub, name, handler, sOpts, permissions, auditImpl) {
223
+ guardEventBusTopic.validate(name);
224
+ var entry = topics.get(name);
225
+ if (!entry) {
226
+ throw new AgentEventBusError("agent-event-bus/unknown-topic",
227
+ "subscribe: topic '" + name + "' not registered");
228
+ }
229
+ if (typeof handler !== "function") {
230
+ throw new AgentEventBusError("agent-event-bus/bad-handler",
231
+ "subscribe: handler must be a function");
232
+ }
233
+ // Permission check for subscribe.
234
+ if (permissions && entry.permissions.subscribe) {
235
+ if (!sOpts.actor) {
236
+ throw new AgentEventBusError("agent-event-bus/no-actor",
237
+ "subscribe: topic '" + name + "' requires actor");
238
+ }
239
+ var allowedSub = false;
240
+ for (var i = 0; i < entry.permissions.subscribe.length; i += 1) {
241
+ if (permissions.check(sOpts.actor, entry.permissions.subscribe[i])) {
242
+ allowedSub = true; break;
243
+ }
244
+ }
245
+ if (!allowedSub) {
246
+ _safeAudit(auditImpl, "agent.event_bus.subscribe_denied", sOpts.actor, { topic: name });
247
+ throw new AgentEventBusError("agent-event-bus/subscribe-denied",
248
+ "subscribe: actor lacks any of " + JSON.stringify(entry.permissions.subscribe) +
249
+ " required for topic '" + name + "'");
250
+ }
251
+ }
252
+ // Cross-tenant subscription gate — when tenantScope is set, the
253
+ // subscriber's actor MUST declare a tenantId at subscribe-time.
254
+ // Subscribers without an actor.tenantId on a tenant-scoped topic
255
+ // are refused outright; the previous shape (filter only when both
256
+ // tenants present) silently accepted such subscribers and let them
257
+ // receive every tenant's events.
258
+ var subscriberTenant = sOpts.actor && sOpts.actor.tenantId ? sOpts.actor.tenantId : null;
259
+ if (entry.tenantScope && !subscriberTenant) {
260
+ _safeAudit(auditImpl, "agent.event_bus.subscribe_denied", sOpts.actor, {
261
+ topic: name, reason: "tenant-scoped-topic-requires-actor-tenant-id",
262
+ });
263
+ throw new AgentEventBusError("agent-event-bus/subscribe-denied",
264
+ "subscribe: tenant-scoped topic '" + name +
265
+ "' requires actor.tenantId; subscribers without a tenant identity are refused");
266
+ }
267
+
268
+ // Wrapped handler: re-validate posture + tenant at delivery so an
269
+ // in-flight tamper / cross-tenant routing attempt is refused at the
270
+ // consumer boundary (not at the bus's trust boundary alone).
271
+ async function _wrappedHandler(wrapped, evMeta) {
272
+ if (!wrapped || typeof wrapped !== "object" || !wrapped._topic) {
273
+ _safeAudit(auditImpl, "agent.event_bus.delivery_dropped", sOpts.actor,
274
+ { topic: name, reason: "malformed-envelope" });
275
+ return;
276
+ }
277
+ // Tenant-scope check: subscriber's tenantId must match the
278
+ // publisher's tenantId from the wire envelope. If the envelope
279
+ // lacks _tenantId (publisher omitted), that's a tampered or
280
+ // malformed wire and the delivery drops.
281
+ if (entry.tenantScope) {
282
+ if (!wrapped._tenantId || wrapped._tenantId !== subscriberTenant) {
283
+ _safeAudit(auditImpl, "agent.event_bus.cross_tenant_drop", sOpts.actor, {
284
+ topic: name,
285
+ publisherTenant: wrapped._tenantId || null,
286
+ subscriberTenant: subscriberTenant,
287
+ reason: wrapped._tenantId ? "tenant-mismatch" : "missing-publisher-tenant",
288
+ });
289
+ return;
290
+ }
291
+ }
292
+ // Re-validate payload against schema in case of in-flight tamper.
293
+ try { guardEventBusPayload.validate(wrapped.payload, entry.schema); }
294
+ catch (_e) {
295
+ _safeAudit(auditImpl, "agent.event_bus.delivery_dropped", sOpts.actor,
296
+ { topic: name, reason: "payload-schema-violation" });
297
+ return;
298
+ }
299
+ // Await handler — supports async handlers + catches their async
300
+ // rejections. Without await, an async handler that rejects would
301
+ // surface as an unhandled rejection and skip the audit emit.
302
+ try {
303
+ await handler(wrapped.payload, {
304
+ topic: name, publishedAt: wrapped._publishedAt,
305
+ source: evMeta && evMeta.source,
306
+ });
307
+ }
308
+ catch (e) {
309
+ _safeAudit(auditImpl, "agent.event_bus.handler_threw", sOpts.actor,
310
+ { topic: name, message: (e && e.message) || String(e) });
311
+ }
312
+ }
313
+ var token = await pubsub.subscribe(name, _wrappedHandler);
314
+ _safeAudit(auditImpl, "agent.event_bus.subscribed", sOpts.actor, { topic: name });
315
+ return function unsubscribe() {
316
+ try {
317
+ if (typeof token === "function") return token();
318
+ if (token && typeof token.unsubscribe === "function") return token.unsubscribe();
319
+ } catch (_e) { /* best-effort */ }
320
+ };
321
+ }
322
+
323
+ // ---- Audit helper ---------------------------------------------------------
324
+
325
+ function _safeAudit(auditImpl, action, actor, metadata) {
326
+ agentAudit.safeAudit(auditImpl, action, actor, metadata);
327
+ }
328
+
329
+ module.exports = {
330
+ create: create,
331
+ AgentEventBusError: AgentEventBusError,
332
+ guards: {
333
+ topic: guardEventBusTopic,
334
+ payload: guardEventBusPayload,
335
+ },
336
+ };
@@ -64,6 +64,7 @@ var { defineClass } = require("./framework-error");
64
64
  var bCrypto = require("./crypto");
65
65
  var safeJson = require("./safe-json");
66
66
  var guardIdempotencyKey = require("./guard-idempotency-key");
67
+ var agentAudit = require("./agent-audit");
67
68
 
68
69
  var audit = lazyRequire(function () { return require("./audit"); });
69
70
 
@@ -331,14 +332,7 @@ function _inMemoryBackend() {
331
332
  }
332
333
 
333
334
  function _safeAudit(auditImpl, action, actor, metadata) {
334
- try {
335
- auditImpl.safeEmit({
336
- action: action,
337
- actor: actor ? { id: actor.id, roles: actor.roles || [] } : { id: "<system>" },
338
- outcome: action.indexOf("denied") >= 0 || action.indexOf("different_args") >= 0 ? "failure" : "success",
339
- metadata: metadata || {},
340
- });
341
- } catch (_e) { /* drop-silent */ }
335
+ agentAudit.safeAudit(auditImpl, action, actor, metadata);
342
336
  }
343
337
 
344
338
  module.exports = {
@@ -56,6 +56,7 @@ var C = require("./constants");
56
56
  var { defineClass } = require("./framework-error");
57
57
  var guardAgentRegistry = require("./guard-agent-registry");
58
58
  var bCrypto = require("./crypto");
59
+ var agentAudit = require("./agent-audit");
59
60
 
60
61
  var audit = lazyRequire(function () { return require("./audit"); });
61
62
  var cluster = lazyRequire(function () { return require("./cluster"); });
@@ -449,14 +450,7 @@ function _checkPermission(ctx, actor, scope) {
449
450
  }
450
451
 
451
452
  function _safeAudit(ctx, action, actor, metadata) {
452
- try {
453
- ctx.audit.safeEmit({
454
- action: action,
455
- actor: actor ? { id: actor.id, roles: actor.roles || [] } : { id: "<system>" },
456
- outcome: action.indexOf("denied") >= 0 || action.indexOf("miss") >= 0 ? "failure" : "success",
457
- metadata: metadata || {},
458
- });
459
- } catch (_e) { /* drop-silent — audit emit failures don't crash the call */ }
453
+ agentAudit.safeAudit(ctx.audit, action, actor, metadata);
460
454
  }
461
455
 
462
456
  module.exports = {