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
package/dist/runtime.js
CHANGED
|
@@ -12,11 +12,17 @@ exports.llmCall = llmCall;
|
|
|
12
12
|
exports.toolCall = toolCall;
|
|
13
13
|
exports.agentStep = agentStep;
|
|
14
14
|
exports.ensureState = ensureState;
|
|
15
|
+
exports.getState = getState;
|
|
15
16
|
exports.emitEvent = emitEvent;
|
|
17
|
+
exports.emitInternal = emitInternal;
|
|
18
|
+
exports.notifyViolation = notifyViolation;
|
|
16
19
|
exports.executeCall = executeCall;
|
|
20
|
+
exports.applyQuotaLimits = applyQuotaLimits;
|
|
17
21
|
exports.buildLlmBody = buildLlmBody;
|
|
18
22
|
exports.buildToolBody = buildToolBody;
|
|
19
23
|
exports.buildAgentBody = buildAgentBody;
|
|
24
|
+
exports.extractUsageMetrics = extractUsageMetrics;
|
|
25
|
+
exports.estimateUsageCost = estimateUsageCost;
|
|
20
26
|
exports.isPromise = isPromise;
|
|
21
27
|
const perf_hooks_1 = require("perf_hooks");
|
|
22
28
|
const logger_1 = require("./logger");
|
|
@@ -39,7 +45,26 @@ const instrumentation_1 = require("./instrumentation");
|
|
|
39
45
|
const telemetry_1 = require("./telemetry");
|
|
40
46
|
const attribution_1 = require("./attribution");
|
|
41
47
|
const config_migrations_1 = require("./config_migrations");
|
|
48
|
+
const quotas_1 = require("./quotas");
|
|
42
49
|
let state = null;
|
|
50
|
+
let cachedVerifyEventHash = null;
|
|
51
|
+
let verifyEventHashResolved = false;
|
|
52
|
+
function getVerifyEventHash() {
|
|
53
|
+
if (verifyEventHashResolved) {
|
|
54
|
+
return cachedVerifyEventHash;
|
|
55
|
+
}
|
|
56
|
+
verifyEventHashResolved = true;
|
|
57
|
+
try {
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
59
|
+
const verifyModule = require('./verify');
|
|
60
|
+
cachedVerifyEventHash = verifyModule.verifyEventHash;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
logger_1.logger.error('Real-time verification unavailable: %s', error);
|
|
64
|
+
cachedVerifyEventHash = null;
|
|
65
|
+
}
|
|
66
|
+
return cachedVerifyEventHash;
|
|
67
|
+
}
|
|
43
68
|
/**
|
|
44
69
|
* Initialize Monora with simplified configuration.
|
|
45
70
|
*
|
|
@@ -56,14 +81,25 @@ let state = null;
|
|
|
56
81
|
* init({ configPath: 'monora.yml' }); // Load from config file
|
|
57
82
|
* init({ configDict: {...} }); // Provide full config dict
|
|
58
83
|
* ```
|
|
84
|
+
*
|
|
85
|
+
* @remarks
|
|
86
|
+
* Cross-SDK Parity: This function is async (returns Promise) in Node.js but
|
|
87
|
+
* synchronous in Python. This is an intentional language difference:
|
|
88
|
+
* - Node.js: `await monora.init({ preset: 'production' })`
|
|
89
|
+
* - Python: `monora.init("production")`
|
|
59
90
|
*/
|
|
60
91
|
async function init(options) {
|
|
61
|
-
const { preset, serviceName, sink, policies, configPath, configDict, envPrefix = 'MONORA_', failFast = false, } = options || {};
|
|
92
|
+
const { preset, serviceName, sink, policies, configPath, configDict, envPrefix = 'MONORA_', failFast = false, onEvent: eventCallback, promptAttribution = false, } = options || {};
|
|
62
93
|
if (state) {
|
|
63
94
|
if (state.traceCompletionUnregister) {
|
|
64
95
|
state.traceCompletionUnregister();
|
|
65
96
|
}
|
|
97
|
+
if (state.traceStartUnregister) {
|
|
98
|
+
state.traceStartUnregister();
|
|
99
|
+
}
|
|
66
100
|
state.dispatcher.close();
|
|
101
|
+
// Clear any previously registered callbacks to avoid duplicates on re-init
|
|
102
|
+
(0, dispatcher_1.clearAuditCallbacks)();
|
|
67
103
|
state = null;
|
|
68
104
|
}
|
|
69
105
|
// Auto-detect environment and service info
|
|
@@ -109,7 +145,23 @@ async function init(options) {
|
|
|
109
145
|
}
|
|
110
146
|
// Check for production readiness warnings
|
|
111
147
|
checkProductionReadiness(config, effectivePreset);
|
|
112
|
-
|
|
148
|
+
enforceOnboardingGate(config);
|
|
149
|
+
// Register user-provided event callback before init
|
|
150
|
+
if (eventCallback) {
|
|
151
|
+
(0, dispatcher_1.onEvent)(eventCallback);
|
|
152
|
+
}
|
|
153
|
+
await initWithConfig(config, failFast, { promptAttribution });
|
|
154
|
+
// Send startup telemetry event (opt-in only)
|
|
155
|
+
if ((0, attribution_1.isTelemetryEnabled)()) {
|
|
156
|
+
(0, attribution_1.sendTelemetryEvent)({
|
|
157
|
+
eventType: 'startup',
|
|
158
|
+
environment: detectedEnv,
|
|
159
|
+
eventData: {
|
|
160
|
+
preset: effectivePreset,
|
|
161
|
+
service_name: config.defaults?.service_name,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
}
|
|
113
165
|
}
|
|
114
166
|
/**
|
|
115
167
|
* Check configuration for production readiness and log warnings.
|
|
@@ -130,10 +182,32 @@ function checkProductionReadiness(config, presetName) {
|
|
|
130
182
|
(0, config_migrations_1.logProductionWarnings)(warnings);
|
|
131
183
|
}
|
|
132
184
|
}
|
|
185
|
+
function enforceOnboardingGate(config) {
|
|
186
|
+
const environment = String(config.defaults?.environment || '').toLowerCase();
|
|
187
|
+
if (environment !== 'production') {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const onboarding = config.onboarding;
|
|
191
|
+
if (!onboarding || typeof onboarding !== 'object') {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (!onboarding.enabled) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (onboarding.required_in_production === false) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const status = String(onboarding.status || 'draft').toLowerCase();
|
|
201
|
+
if (status === 'completed') {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
throw new Error('Onboarding is required for production but is not completed. ' +
|
|
205
|
+
'Run `npx monora-ai onboard validate` and `npx monora-ai onboard complete` first.');
|
|
206
|
+
}
|
|
133
207
|
/**
|
|
134
208
|
* Initialize Monora with a pre-built configuration.
|
|
135
209
|
*/
|
|
136
|
-
async function initWithConfig(config, failFast) {
|
|
210
|
+
async function initWithConfig(config, failFast, options) {
|
|
137
211
|
const sinks = buildSinks(config.sinks || [], failFast);
|
|
138
212
|
const dispatcher = new dispatcher_1.EventDispatcher(sinks, config);
|
|
139
213
|
const registry = new registry_1.ModelRegistry(config.registry || {});
|
|
@@ -153,7 +227,9 @@ async function initWithConfig(config, failFast) {
|
|
|
153
227
|
reportManager,
|
|
154
228
|
wal,
|
|
155
229
|
signer,
|
|
230
|
+
quotaManager: new quotas_1.QuotaManager(config.quotas || {}),
|
|
156
231
|
};
|
|
232
|
+
state.emitInternal = emitInternal;
|
|
157
233
|
// Recover uncommitted events from WAL
|
|
158
234
|
if (config.wal?.enabled && config.wal?.recovery_on_startup !== false) {
|
|
159
235
|
try {
|
|
@@ -176,8 +252,11 @@ async function initWithConfig(config, failFast) {
|
|
|
176
252
|
}
|
|
177
253
|
}
|
|
178
254
|
}
|
|
255
|
+
state.traceStartUnregister = (0, context_1.registerTraceStartHandler)((span) => {
|
|
256
|
+
handleTraceStart(span);
|
|
257
|
+
});
|
|
179
258
|
state.traceCompletionUnregister = (0, context_1.registerTraceCompletionHandler)((span) => {
|
|
180
|
-
handleTraceCompletion(span
|
|
259
|
+
handleTraceCompletion(span);
|
|
181
260
|
});
|
|
182
261
|
// Initialize telemetry/metrics collection
|
|
183
262
|
await (0, telemetry_1.initMetrics)({ telemetry: config.telemetry });
|
|
@@ -200,6 +279,18 @@ async function initWithConfig(config, failFast) {
|
|
|
200
279
|
}
|
|
201
280
|
dispatcher.start();
|
|
202
281
|
applyAttributionConfig(config);
|
|
282
|
+
const envValue = config.defaults?.environment || undefined;
|
|
283
|
+
if (options?.promptAttribution) {
|
|
284
|
+
await (0, attribution_1.promptForRegistration)({
|
|
285
|
+
environment: envValue,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
// Show first-run registration prompt if needed (one-time only)
|
|
290
|
+
(0, attribution_1.showFirstRunPromptIfNeeded)({
|
|
291
|
+
environment: envValue,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
203
294
|
emitSdkInit(state);
|
|
204
295
|
}
|
|
205
296
|
function getSdkVersion() {
|
|
@@ -251,9 +342,9 @@ function applyAttributionConfig(config) {
|
|
|
251
342
|
}
|
|
252
343
|
: undefined,
|
|
253
344
|
apiKeyEnv: telemetry.api_key_env ?? undefined,
|
|
254
|
-
|
|
345
|
+
timeoutSec: typeof telemetry.timeout_sec === 'number' ? telemetry.timeout_sec : undefined,
|
|
255
346
|
retryAttempts: typeof telemetry.retry_attempts === 'number' ? telemetry.retry_attempts : undefined,
|
|
256
|
-
|
|
347
|
+
backoffBaseSec: typeof telemetry.backoff_base_sec === 'number' ? telemetry.backoff_base_sec : undefined,
|
|
257
348
|
});
|
|
258
349
|
if (telemetry.enabled || telemetry.send_data || attribution.enabled) {
|
|
259
350
|
(0, attribution_1.enableTelemetry)({
|
|
@@ -407,11 +498,26 @@ function recordFeatureFlags(config, attribution) {
|
|
|
407
498
|
* 1. Verifies all pending trace chains (if verify_on_shutdown enabled)
|
|
408
499
|
* 2. Finalizes all pending traces and emits report summaries
|
|
409
500
|
* 3. Closes all sinks and dispatchers
|
|
501
|
+
*
|
|
502
|
+
* @remarks
|
|
503
|
+
* Cross-SDK Parity: This function is async (returns Promise) in Node.js but
|
|
504
|
+
* synchronous in Python. This is an intentional language difference:
|
|
505
|
+
* - Node.js: `await monora.shutdown()`
|
|
506
|
+
* - Python: `monora.shutdown()`
|
|
410
507
|
*/
|
|
411
508
|
async function shutdown() {
|
|
412
509
|
if (!state) {
|
|
413
510
|
return;
|
|
414
511
|
}
|
|
512
|
+
// Send shutdown telemetry event (opt-in only)
|
|
513
|
+
if ((0, attribution_1.isTelemetryEnabled)()) {
|
|
514
|
+
(0, attribution_1.sendTelemetryEvent)({
|
|
515
|
+
eventType: 'shutdown',
|
|
516
|
+
eventData: {
|
|
517
|
+
service_name: state.config.defaults?.service_name,
|
|
518
|
+
},
|
|
519
|
+
});
|
|
520
|
+
}
|
|
415
521
|
// AUTOMATIC VERIFICATION ON SHUTDOWN
|
|
416
522
|
const immutability = state.config.immutability || {};
|
|
417
523
|
if (immutability.verify_on_shutdown !== false) {
|
|
@@ -420,6 +526,9 @@ async function shutdown() {
|
|
|
420
526
|
if (state.traceCompletionUnregister) {
|
|
421
527
|
state.traceCompletionUnregister();
|
|
422
528
|
}
|
|
529
|
+
if (state.traceStartUnregister) {
|
|
530
|
+
state.traceStartUnregister();
|
|
531
|
+
}
|
|
423
532
|
state.dispatcher.close();
|
|
424
533
|
// Cleanup and close WAL
|
|
425
534
|
if (state.config.wal?.enabled) {
|
|
@@ -456,7 +565,23 @@ function exportTrustPackage(traceId, options) {
|
|
|
456
565
|
if (!events) {
|
|
457
566
|
throw new Error('No events found for trust package; provide events, inputPath, or configure a file sink.');
|
|
458
567
|
}
|
|
459
|
-
let trustPackage = (0, trust_package_1.buildTrustPackage)(traceId, events, config
|
|
568
|
+
let trustPackage = (0, trust_package_1.buildTrustPackage)(traceId, events, config, {
|
|
569
|
+
evidenceManifest: options?.evidenceManifest,
|
|
570
|
+
evidenceManifestPath: options?.evidenceManifestPath,
|
|
571
|
+
evidenceManifestAuto: options?.evidenceManifestAuto,
|
|
572
|
+
evidenceManifestStandard: options?.evidenceManifestStandard,
|
|
573
|
+
evidenceManifestIncludeLineage: options?.evidenceManifestIncludeLineage,
|
|
574
|
+
evidenceManifestIncludeHashChain: options?.evidenceManifestIncludeHashChain,
|
|
575
|
+
evidenceManifestIncludeWorkflowState: options?.evidenceManifestIncludeWorkflowState,
|
|
576
|
+
evidenceManifestIncludeAimsState: options?.evidenceManifestIncludeAimsState,
|
|
577
|
+
controlCatalog: options?.controlCatalog,
|
|
578
|
+
controlCatalogPath: options?.controlCatalogPath,
|
|
579
|
+
controlCatalogStandard: options?.controlCatalogStandard,
|
|
580
|
+
controlWorkflowState: options?.controlWorkflowState,
|
|
581
|
+
controlWorkflowStatePath: options?.controlWorkflowStatePath,
|
|
582
|
+
controlCoverageTarget: options?.controlCoverageTarget,
|
|
583
|
+
controlCoveragePath: options?.controlCoveragePath,
|
|
584
|
+
});
|
|
460
585
|
if (options?.sign || options?.gpgKey || options?.gpgHome) {
|
|
461
586
|
trustPackage = (0, trust_package_1.applyGpgSignature)(trustPackage, {
|
|
462
587
|
gpgKey: options?.gpgKey,
|
|
@@ -549,6 +674,13 @@ function ensureState() {
|
|
|
549
674
|
}
|
|
550
675
|
return state;
|
|
551
676
|
}
|
|
677
|
+
/**
|
|
678
|
+
* Get the current Monora state, or null if not initialized.
|
|
679
|
+
* Use this for optional event emission where initialization isn't required.
|
|
680
|
+
*/
|
|
681
|
+
function getState() {
|
|
682
|
+
return state;
|
|
683
|
+
}
|
|
552
684
|
function isRecoveredEventValid(event, config) {
|
|
553
685
|
if (!event || typeof event !== 'object') {
|
|
554
686
|
return false;
|
|
@@ -640,7 +772,7 @@ function emitEvent(event, options) {
|
|
|
640
772
|
}
|
|
641
773
|
return;
|
|
642
774
|
}
|
|
643
|
-
if ((0, context_1.getCurrentSpan)()
|
|
775
|
+
if ((0, context_1.getCurrentSpan)()) {
|
|
644
776
|
try {
|
|
645
777
|
current.reportManager.recordEvent(event);
|
|
646
778
|
}
|
|
@@ -650,6 +782,38 @@ function emitEvent(event, options) {
|
|
|
650
782
|
}
|
|
651
783
|
current.dispatcher.emit(event);
|
|
652
784
|
}
|
|
785
|
+
function emitInternal(payload, body, options) {
|
|
786
|
+
const current = getState();
|
|
787
|
+
if (!current) {
|
|
788
|
+
return null;
|
|
789
|
+
}
|
|
790
|
+
const raw = typeof payload === 'object' && payload ? payload : {};
|
|
791
|
+
const eventType = (typeof payload === 'string' ? payload : undefined) ||
|
|
792
|
+
raw.event_type ||
|
|
793
|
+
'custom';
|
|
794
|
+
const eventBody = body !== undefined ? body : (raw.body || {});
|
|
795
|
+
const normalizedBody = eventBody && typeof eventBody === 'object' && !Array.isArray(eventBody)
|
|
796
|
+
? eventBody
|
|
797
|
+
: { value: eventBody };
|
|
798
|
+
const built = current.eventBuilder.build(eventType, normalizedBody, {
|
|
799
|
+
dataClassification: options?.dataClassification ||
|
|
800
|
+
raw.data_classification ||
|
|
801
|
+
current.config.defaults?.data_classification ||
|
|
802
|
+
'internal',
|
|
803
|
+
purpose: options?.purpose ||
|
|
804
|
+
raw.purpose ||
|
|
805
|
+
current.config.defaults?.purpose ||
|
|
806
|
+
'governance',
|
|
807
|
+
reason: options?.reason || raw.reason || undefined,
|
|
808
|
+
parentEventId: options?.parentEventId || raw.parent_event_id || undefined,
|
|
809
|
+
});
|
|
810
|
+
const traceId = options?.traceId || raw.trace_id;
|
|
811
|
+
if (traceId) {
|
|
812
|
+
built.trace_id = traceId;
|
|
813
|
+
}
|
|
814
|
+
emitEvent(built);
|
|
815
|
+
return built;
|
|
816
|
+
}
|
|
653
817
|
/**
|
|
654
818
|
* Verify event hash in real-time as it's emitted.
|
|
655
819
|
*
|
|
@@ -657,8 +821,10 @@ function emitEvent(event, options) {
|
|
|
657
821
|
* Failures are logged as warnings (user decision: don't block).
|
|
658
822
|
*/
|
|
659
823
|
function verifyEventRealTime(event, expectedPrevHash) {
|
|
660
|
-
|
|
661
|
-
|
|
824
|
+
const verifyEventHash = getVerifyEventHash();
|
|
825
|
+
if (!verifyEventHash) {
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
662
828
|
try {
|
|
663
829
|
if (!verifyEventHash(event)) {
|
|
664
830
|
logger_1.logger.error('Real-time verification FAILED: event %s has invalid hash', event.event_id);
|
|
@@ -673,8 +839,8 @@ function verifyEventRealTime(event, expectedPrevHash) {
|
|
|
673
839
|
}
|
|
674
840
|
function notifyViolation(violation) {
|
|
675
841
|
const current = ensureState();
|
|
676
|
-
// Record violation metric
|
|
677
|
-
const policyType = violation.
|
|
842
|
+
// Record violation metric (policyType matches Python SDK's policy_type attribute)
|
|
843
|
+
const policyType = violation.policyType || 'model_policy';
|
|
678
844
|
const model = violation.model || 'unknown';
|
|
679
845
|
(0, telemetry_1.recordViolation)(policyType, model);
|
|
680
846
|
if (!current.violationHandler) {
|
|
@@ -687,14 +853,43 @@ function notifyViolation(violation) {
|
|
|
687
853
|
logger_1.logger.error('Violation handler error: %s', error);
|
|
688
854
|
}
|
|
689
855
|
}
|
|
690
|
-
function
|
|
856
|
+
function emitSpanEvent(current, span, eventType, durationMs) {
|
|
857
|
+
const tracingConfig = current.config.tracing || {};
|
|
858
|
+
if (tracingConfig.emit_span_events === false) {
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
const body = {
|
|
862
|
+
name: span.name,
|
|
863
|
+
metadata: span.metadata,
|
|
864
|
+
};
|
|
865
|
+
if (durationMs !== undefined) {
|
|
866
|
+
body.duration_ms = durationMs;
|
|
867
|
+
}
|
|
868
|
+
const event = current.eventBuilder.build(eventType, body);
|
|
869
|
+
emitEvent(event);
|
|
870
|
+
}
|
|
871
|
+
function handleTraceStart(span) {
|
|
872
|
+
const current = state;
|
|
873
|
+
if (!current) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
emitSpanEvent(current, span, 'span_start');
|
|
877
|
+
}
|
|
878
|
+
function handleTraceCompletion(span) {
|
|
691
879
|
const current = state;
|
|
692
|
-
if (!current
|
|
880
|
+
if (!current) {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
const traceStart = (0, context_1.getTraceStartTime)(span.traceId);
|
|
884
|
+
const durationMs = traceStart !== undefined ? Math.max(0, Date.now() - traceStart) : undefined;
|
|
885
|
+
emitSpanEvent(current, span, 'span_end', durationMs);
|
|
886
|
+
current.quotaManager.removeTrace(span.traceId);
|
|
887
|
+
if (!current.reportManager.isEnabled()) {
|
|
693
888
|
return;
|
|
694
889
|
}
|
|
695
890
|
try {
|
|
696
891
|
const registryMetadata = buildRegistryMetadata(current.registry);
|
|
697
|
-
const result = current.reportManager.finalizeTrace(traceId, registryMetadata);
|
|
892
|
+
const result = current.reportManager.finalizeTrace(span.traceId, registryMetadata);
|
|
698
893
|
if (!result) {
|
|
699
894
|
return;
|
|
700
895
|
}
|
|
@@ -755,6 +950,8 @@ function executeCall(current, fn, args, config, context) {
|
|
|
755
950
|
const purpose = options.purpose || current.config.defaults?.purpose || 'general';
|
|
756
951
|
const reason = options.reason;
|
|
757
952
|
const resolvedModel = options.model || resolveModelFromArgs(args, current.registry);
|
|
953
|
+
const span = (0, context_1.getCurrentSpan)();
|
|
954
|
+
const traceId = span ? span.traceId : null;
|
|
758
955
|
if (config.eventType === 'tool_call' && options.toolName) {
|
|
759
956
|
try {
|
|
760
957
|
const violation = current.toolPolicyEngine.checkTool(options.toolName);
|
|
@@ -870,6 +1067,9 @@ function executeCall(current, fn, args, config, context) {
|
|
|
870
1067
|
throw violation;
|
|
871
1068
|
}
|
|
872
1069
|
}
|
|
1070
|
+
if (config.eventType === 'llm_call') {
|
|
1071
|
+
applyQuotaLimits(current, traceId, resolvedModel, dataClassification, purpose, reason);
|
|
1072
|
+
}
|
|
873
1073
|
const startTime = perf_hooks_1.performance.now();
|
|
874
1074
|
const stepNumber = config.eventType === 'agent_step' ? (0, context_1.nextStepNumber)() : undefined;
|
|
875
1075
|
try {
|
|
@@ -899,9 +1099,17 @@ function executeCall(current, fn, args, config, context) {
|
|
|
899
1099
|
reason,
|
|
900
1100
|
});
|
|
901
1101
|
emitEvent(event);
|
|
1102
|
+
if (config.eventType === 'llm_call') {
|
|
1103
|
+
const { tokens, usage } = extractUsageMetrics(response);
|
|
1104
|
+
const cost = estimateUsageCost(current, resolvedModel, usage, tokens);
|
|
1105
|
+
applyQuotaLimits(current, traceId, resolvedModel, dataClassification, purpose, reason, tokens || [0, 0], cost ?? undefined);
|
|
1106
|
+
}
|
|
902
1107
|
return response;
|
|
903
1108
|
})
|
|
904
1109
|
.catch((error) => {
|
|
1110
|
+
if (error instanceof quotas_1.QuotaExceededError) {
|
|
1111
|
+
throw error;
|
|
1112
|
+
}
|
|
905
1113
|
const durationMs = perf_hooks_1.performance.now() - startTime;
|
|
906
1114
|
const logErrors = current.config.error_handling?.log_user_exceptions !== false;
|
|
907
1115
|
const body = config.buildBody({
|
|
@@ -925,6 +1133,9 @@ function executeCall(current, fn, args, config, context) {
|
|
|
925
1133
|
reason,
|
|
926
1134
|
});
|
|
927
1135
|
emitEvent(event);
|
|
1136
|
+
if (config.eventType === 'llm_call') {
|
|
1137
|
+
applyQuotaLimits(current, traceId, resolvedModel, dataClassification, purpose, reason, [0, 0], undefined, false);
|
|
1138
|
+
}
|
|
928
1139
|
throw error;
|
|
929
1140
|
});
|
|
930
1141
|
}
|
|
@@ -950,10 +1161,17 @@ function executeCall(current, fn, args, config, context) {
|
|
|
950
1161
|
reason,
|
|
951
1162
|
});
|
|
952
1163
|
emitEvent(event);
|
|
1164
|
+
if (config.eventType === 'llm_call') {
|
|
1165
|
+
const { tokens, usage } = extractUsageMetrics(result);
|
|
1166
|
+
const cost = estimateUsageCost(current, resolvedModel, usage, tokens);
|
|
1167
|
+
applyQuotaLimits(current, traceId, resolvedModel, dataClassification, purpose, reason, tokens || [0, 0], cost ?? undefined);
|
|
1168
|
+
}
|
|
953
1169
|
return result;
|
|
954
1170
|
}
|
|
955
1171
|
catch (error) {
|
|
956
|
-
if (error instanceof data_handling_1.DataHandlingViolation ||
|
|
1172
|
+
if (error instanceof data_handling_1.DataHandlingViolation ||
|
|
1173
|
+
error instanceof policy_1.PolicyViolation ||
|
|
1174
|
+
error instanceof quotas_1.QuotaExceededError) {
|
|
957
1175
|
throw error;
|
|
958
1176
|
}
|
|
959
1177
|
const durationMs = perf_hooks_1.performance.now() - startTime;
|
|
@@ -979,6 +1197,9 @@ function executeCall(current, fn, args, config, context) {
|
|
|
979
1197
|
reason,
|
|
980
1198
|
});
|
|
981
1199
|
emitEvent(event);
|
|
1200
|
+
if (config.eventType === 'llm_call') {
|
|
1201
|
+
applyQuotaLimits(current, traceId, resolvedModel, dataClassification, purpose, reason, [0, 0], undefined, false);
|
|
1202
|
+
}
|
|
982
1203
|
throw error;
|
|
983
1204
|
}
|
|
984
1205
|
}
|
|
@@ -1030,6 +1251,48 @@ function emitRegistryOverride(current, model, dataClassification, purpose, reaso
|
|
|
1030
1251
|
emitEvent(event);
|
|
1031
1252
|
return event.event_id || null;
|
|
1032
1253
|
}
|
|
1254
|
+
function applyQuotaLimits(current, traceId, model, dataClassification, purpose, reason, tokens, cost, raiseOnBlock = true) {
|
|
1255
|
+
if (!traceId || !current.quotaManager.isEnabled()) {
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1258
|
+
const tracker = current.quotaManager.getTracker(traceId);
|
|
1259
|
+
if (tokens || cost !== undefined) {
|
|
1260
|
+
const [promptTokens, completionTokens] = tokens || [0, 0];
|
|
1261
|
+
tracker.recordTokenUsage(promptTokens, completionTokens, cost);
|
|
1262
|
+
}
|
|
1263
|
+
const result = tracker.checkQuotas();
|
|
1264
|
+
if (result.allowed) {
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
emitQuotaViolation(current.eventBuilder, model, result, dataClassification, purpose, reason);
|
|
1268
|
+
const violation = new quotas_1.QuotaExceededError(result.exceededQuota || 'unknown', result.currentValue || 0, result.limitValue || 0, model);
|
|
1269
|
+
notifyViolation(violation);
|
|
1270
|
+
const action = tracker.getConfig().action;
|
|
1271
|
+
if (action === 'block' && raiseOnBlock) {
|
|
1272
|
+
throw violation;
|
|
1273
|
+
}
|
|
1274
|
+
logger_1.logger.warning('Quota warning: %s', result.message);
|
|
1275
|
+
}
|
|
1276
|
+
function emitQuotaViolation(builder, model, result, dataClassification, purpose, reason) {
|
|
1277
|
+
const body = {
|
|
1278
|
+
model,
|
|
1279
|
+
policy_name: `quota.${result.exceededQuota || 'unknown'}`,
|
|
1280
|
+
message: result.message,
|
|
1281
|
+
status: 'policy_violation',
|
|
1282
|
+
error: null,
|
|
1283
|
+
quota: {
|
|
1284
|
+
type: result.exceededQuota ?? null,
|
|
1285
|
+
current_value: result.currentValue ?? null,
|
|
1286
|
+
limit_value: result.limitValue ?? null,
|
|
1287
|
+
},
|
|
1288
|
+
};
|
|
1289
|
+
const event = builder.build('llm_call', body, {
|
|
1290
|
+
dataClassification,
|
|
1291
|
+
purpose,
|
|
1292
|
+
reason,
|
|
1293
|
+
});
|
|
1294
|
+
emitEvent(event);
|
|
1295
|
+
}
|
|
1033
1296
|
function buildLlmBody(params) {
|
|
1034
1297
|
const responseDetails = params.response ? extractResponseDetails(params.response) : null;
|
|
1035
1298
|
let explanation;
|
|
@@ -1101,6 +1364,132 @@ function extractResponseDetails(response) {
|
|
|
1101
1364
|
}
|
|
1102
1365
|
return { content, finish_reason: finishReason, usage };
|
|
1103
1366
|
}
|
|
1367
|
+
function extractUsageMetrics(response) {
|
|
1368
|
+
if (!response) {
|
|
1369
|
+
return { tokens: null, usage: null };
|
|
1370
|
+
}
|
|
1371
|
+
const details = extractResponseDetails(response);
|
|
1372
|
+
const usage = details && typeof details === 'object' ? details.usage : null;
|
|
1373
|
+
return { tokens: normalizeTokenUsage(usage), usage };
|
|
1374
|
+
}
|
|
1375
|
+
function estimateUsageCost(current, model, usage, tokens) {
|
|
1376
|
+
if (!usage || typeof usage !== 'object') {
|
|
1377
|
+
return null;
|
|
1378
|
+
}
|
|
1379
|
+
for (const key of ['total_cost', 'cost', 'estimated_cost', 'total_cost_usd', 'cost_usd']) {
|
|
1380
|
+
const value = usage[key];
|
|
1381
|
+
const cost = coerceCost(value);
|
|
1382
|
+
if (cost !== null) {
|
|
1383
|
+
return cost;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
if (!model || !tokens) {
|
|
1387
|
+
return null;
|
|
1388
|
+
}
|
|
1389
|
+
const metadata = current.registry.getModelMetadata(model);
|
|
1390
|
+
const pricing = metadata && typeof metadata === 'object' ? metadata.pricing : null;
|
|
1391
|
+
return estimateCostFromPricing(pricing, tokens);
|
|
1392
|
+
}
|
|
1393
|
+
function estimateCostFromPricing(pricing, tokens) {
|
|
1394
|
+
if (!pricing || typeof pricing !== 'object') {
|
|
1395
|
+
return null;
|
|
1396
|
+
}
|
|
1397
|
+
const [promptTokens, completionTokens] = tokens;
|
|
1398
|
+
const pickRatePerToken = (candidates) => {
|
|
1399
|
+
for (const candidate of candidates) {
|
|
1400
|
+
if (!(candidate.key in pricing)) {
|
|
1401
|
+
continue;
|
|
1402
|
+
}
|
|
1403
|
+
const cost = coerceCost(pricing[candidate.key]);
|
|
1404
|
+
if (cost === null) {
|
|
1405
|
+
continue;
|
|
1406
|
+
}
|
|
1407
|
+
return cost / candidate.scale;
|
|
1408
|
+
}
|
|
1409
|
+
return null;
|
|
1410
|
+
};
|
|
1411
|
+
const totalRate = pickRatePerToken([
|
|
1412
|
+
{ key: 'cost_per_token', scale: 1 },
|
|
1413
|
+
{ key: 'cost_per_1k_tokens', scale: 1000 },
|
|
1414
|
+
{ key: 'total_cost_per_1k', scale: 1000 },
|
|
1415
|
+
{ key: 'cost_per_1m_tokens', scale: 1000000 },
|
|
1416
|
+
{ key: 'total_cost_per_1m', scale: 1000000 },
|
|
1417
|
+
]);
|
|
1418
|
+
const promptRate = pickRatePerToken([
|
|
1419
|
+
{ key: 'prompt_cost_per_token', scale: 1 },
|
|
1420
|
+
{ key: 'input_cost_per_token', scale: 1 },
|
|
1421
|
+
{ key: 'prompt_cost_per_1k', scale: 1000 },
|
|
1422
|
+
{ key: 'input_cost_per_1k', scale: 1000 },
|
|
1423
|
+
{ key: 'prompt_cost_per_1m', scale: 1000000 },
|
|
1424
|
+
{ key: 'input_cost_per_1m', scale: 1000000 },
|
|
1425
|
+
]);
|
|
1426
|
+
const completionRate = pickRatePerToken([
|
|
1427
|
+
{ key: 'completion_cost_per_token', scale: 1 },
|
|
1428
|
+
{ key: 'output_cost_per_token', scale: 1 },
|
|
1429
|
+
{ key: 'completion_cost_per_1k', scale: 1000 },
|
|
1430
|
+
{ key: 'output_cost_per_1k', scale: 1000 },
|
|
1431
|
+
{ key: 'completion_cost_per_1m', scale: 1000000 },
|
|
1432
|
+
{ key: 'output_cost_per_1m', scale: 1000000 },
|
|
1433
|
+
]);
|
|
1434
|
+
if (totalRate !== null) {
|
|
1435
|
+
return (promptTokens + completionTokens) * totalRate;
|
|
1436
|
+
}
|
|
1437
|
+
if (promptRate === null && completionRate === null) {
|
|
1438
|
+
return null;
|
|
1439
|
+
}
|
|
1440
|
+
let cost = 0;
|
|
1441
|
+
if (promptRate !== null) {
|
|
1442
|
+
cost += promptTokens * promptRate;
|
|
1443
|
+
}
|
|
1444
|
+
if (completionRate !== null) {
|
|
1445
|
+
cost += completionTokens * completionRate;
|
|
1446
|
+
}
|
|
1447
|
+
return cost;
|
|
1448
|
+
}
|
|
1449
|
+
function normalizeTokenUsage(usage) {
|
|
1450
|
+
if (!usage || typeof usage !== 'object') {
|
|
1451
|
+
return null;
|
|
1452
|
+
}
|
|
1453
|
+
let prompt = coerceToken(usage.prompt_tokens);
|
|
1454
|
+
let completion = coerceToken(usage.completion_tokens);
|
|
1455
|
+
if (prompt === null) {
|
|
1456
|
+
prompt = coerceToken(usage.input_tokens);
|
|
1457
|
+
}
|
|
1458
|
+
if (completion === null) {
|
|
1459
|
+
completion = coerceToken(usage.output_tokens);
|
|
1460
|
+
}
|
|
1461
|
+
const total = coerceToken(usage.total_tokens);
|
|
1462
|
+
if (prompt === null && completion === null && total === null) {
|
|
1463
|
+
return null;
|
|
1464
|
+
}
|
|
1465
|
+
if (prompt === null) {
|
|
1466
|
+
prompt = Math.max(0, (total || 0) - (completion || 0));
|
|
1467
|
+
}
|
|
1468
|
+
if (completion === null) {
|
|
1469
|
+
completion = Math.max(0, (total || 0) - (prompt || 0));
|
|
1470
|
+
}
|
|
1471
|
+
return [prompt || 0, completion || 0];
|
|
1472
|
+
}
|
|
1473
|
+
function coerceToken(value) {
|
|
1474
|
+
if (value === null || value === undefined) {
|
|
1475
|
+
return null;
|
|
1476
|
+
}
|
|
1477
|
+
const number = Number(value);
|
|
1478
|
+
if (!Number.isFinite(number)) {
|
|
1479
|
+
return null;
|
|
1480
|
+
}
|
|
1481
|
+
return Math.trunc(number);
|
|
1482
|
+
}
|
|
1483
|
+
function coerceCost(value) {
|
|
1484
|
+
if (value === null || value === undefined) {
|
|
1485
|
+
return null;
|
|
1486
|
+
}
|
|
1487
|
+
const number = Number(value);
|
|
1488
|
+
if (!Number.isFinite(number)) {
|
|
1489
|
+
return null;
|
|
1490
|
+
}
|
|
1491
|
+
return number;
|
|
1492
|
+
}
|
|
1104
1493
|
function formatError(error) {
|
|
1105
1494
|
return { type: error.name || 'Error', message: error.message || String(error) };
|
|
1106
1495
|
}
|
|
@@ -1180,7 +1569,7 @@ function buildSinks(sinkConfigs, failFast) {
|
|
|
1180
1569
|
const sinkType = String(config.type || '').toLowerCase();
|
|
1181
1570
|
try {
|
|
1182
1571
|
if (sinkType === 'stdout') {
|
|
1183
|
-
sinks.push(new sinks_1.StdoutSink(config.format || '
|
|
1572
|
+
sinks.push(new sinks_1.StdoutSink(config.format || 'pretty'));
|
|
1184
1573
|
}
|
|
1185
1574
|
else if (sinkType === 'file') {
|
|
1186
1575
|
sinks.push(new sinks_1.FileSink(config.path, {
|
|
@@ -1188,6 +1577,7 @@ function buildSinks(sinkConfigs, failFast) {
|
|
|
1188
1577
|
flushIntervalSec: config.flush_interval_sec || 5.0,
|
|
1189
1578
|
rotation: config.rotation || 'none',
|
|
1190
1579
|
maxSizeMb: config.max_size_mb,
|
|
1580
|
+
symlink: config.symlink,
|
|
1191
1581
|
}));
|
|
1192
1582
|
}
|
|
1193
1583
|
else if (sinkType === 'https' || sinkType === 'http') {
|
|
@@ -1206,6 +1596,12 @@ function buildSinks(sinkConfigs, failFast) {
|
|
|
1206
1596
|
headerName: config.idempotency.header_name,
|
|
1207
1597
|
}
|
|
1208
1598
|
: undefined;
|
|
1599
|
+
const dataResidencyConfig = config.data_residency
|
|
1600
|
+
? {
|
|
1601
|
+
residency: config.data_residency,
|
|
1602
|
+
action: config.data_residency_action,
|
|
1603
|
+
}
|
|
1604
|
+
: undefined;
|
|
1209
1605
|
sinks.push(new sinks_1.HttpSink(config.endpoint, headers, {
|
|
1210
1606
|
batchSize: config.batch_size || 50,
|
|
1211
1607
|
timeoutSec: config.timeout_sec || 10.0,
|
|
@@ -1214,6 +1610,7 @@ function buildSinks(sinkConfigs, failFast) {
|
|
|
1214
1610
|
circuitBreaker: config.circuit_breaker,
|
|
1215
1611
|
retryQueue: retryQueueConfig,
|
|
1216
1612
|
idempotency: idempotencyConfig,
|
|
1613
|
+
dataResidency: dataResidencyConfig,
|
|
1217
1614
|
}));
|
|
1218
1615
|
}
|
|
1219
1616
|
else if (sinkType) {
|
|
@@ -1252,5 +1649,6 @@ function findEventSource(config) {
|
|
|
1252
1649
|
return String(sink.path);
|
|
1253
1650
|
}
|
|
1254
1651
|
}
|
|
1255
|
-
|
|
1652
|
+
const fallback = config.error_handling?.fallback_path;
|
|
1653
|
+
return fallback ? String(fallback) : undefined;
|
|
1256
1654
|
}
|