@kya-os/mcp-i-cloudflare 1.12.3 → 1.13.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.
Files changed (79) hide show
  1. package/README.md +5 -1
  2. package/dist/agent.d.ts +1 -1
  3. package/dist/agent.d.ts.map +1 -1
  4. package/dist/agent.js +23 -24
  5. package/dist/agent.js.map +1 -1
  6. package/dist/app.d.ts.map +1 -1
  7. package/dist/app.js +23 -9
  8. package/dist/app.js.map +1 -1
  9. package/dist/audit/assurance.d.ts +12 -0
  10. package/dist/audit/assurance.d.ts.map +1 -0
  11. package/dist/audit/assurance.js +21 -0
  12. package/dist/audit/assurance.js.map +1 -0
  13. package/dist/audit/deployment-binding.d.ts +513 -0
  14. package/dist/audit/deployment-binding.d.ts.map +1 -0
  15. package/dist/audit/deployment-binding.js +364 -0
  16. package/dist/audit/deployment-binding.js.map +1 -0
  17. package/dist/audit/durable-producer.d.ts +151 -0
  18. package/dist/audit/durable-producer.d.ts.map +1 -0
  19. package/dist/audit/durable-producer.js +953 -0
  20. package/dist/audit/durable-producer.js.map +1 -0
  21. package/dist/audit/verified-recorder-client.d.ts +34 -0
  22. package/dist/audit/verified-recorder-client.d.ts.map +1 -0
  23. package/dist/audit/verified-recorder-client.js +192 -0
  24. package/dist/audit/verified-recorder-client.js.map +1 -0
  25. package/dist/config.d.ts +8 -3
  26. package/dist/config.d.ts.map +1 -1
  27. package/dist/config.js +9 -3
  28. package/dist/config.js.map +1 -1
  29. package/dist/delegation-http/consent-anchor-bridge.d.ts +18 -1
  30. package/dist/delegation-http/consent-anchor-bridge.d.ts.map +1 -1
  31. package/dist/delegation-http/consent-anchor-bridge.js +18 -5
  32. package/dist/delegation-http/consent-anchor-bridge.js.map +1 -1
  33. package/dist/delegation-http/pickup-routes.d.ts.map +1 -1
  34. package/dist/delegation-http/pickup-routes.js +36 -3
  35. package/dist/delegation-http/pickup-routes.js.map +1 -1
  36. package/dist/delegation-http/well-known-capability.d.ts +2 -1
  37. package/dist/delegation-http/well-known-capability.d.ts.map +1 -1
  38. package/dist/delegation-http/well-known-capability.js +2 -1
  39. package/dist/delegation-http/well-known-capability.js.map +1 -1
  40. package/dist/helpers/env-mapper.d.ts.map +1 -1
  41. package/dist/helpers/env-mapper.js +27 -0
  42. package/dist/helpers/env-mapper.js.map +1 -1
  43. package/dist/index.d.ts +8 -6
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +14 -2
  46. package/dist/index.js.map +1 -1
  47. package/dist/runtime/invoke-tool-handler.d.ts +10 -0
  48. package/dist/runtime/invoke-tool-handler.d.ts.map +1 -0
  49. package/dist/runtime/invoke-tool-handler.js +11 -0
  50. package/dist/runtime/invoke-tool-handler.js.map +1 -0
  51. package/dist/runtime.d.ts +1 -6
  52. package/dist/runtime.d.ts.map +1 -1
  53. package/dist/runtime.js +72 -72
  54. package/dist/runtime.js.map +1 -1
  55. package/dist/services/consent-audit.service.d.ts +3 -10
  56. package/dist/services/consent-audit.service.d.ts.map +1 -1
  57. package/dist/services/consent-audit.service.js +44 -77
  58. package/dist/services/consent-audit.service.js.map +1 -1
  59. package/dist/services/consent-templates/template-renderer.d.ts.map +1 -1
  60. package/dist/services/consent-templates/template-renderer.js +6 -4
  61. package/dist/services/consent-templates/template-renderer.js.map +1 -1
  62. package/dist/services/consent.service.d.ts +17 -2
  63. package/dist/services/consent.service.d.ts.map +1 -1
  64. package/dist/services/consent.service.js +53 -30
  65. package/dist/services/consent.service.js.map +1 -1
  66. package/dist/services/proof.service.d.ts.map +1 -1
  67. package/dist/services/proof.service.js +20 -4
  68. package/dist/services/proof.service.js.map +1 -1
  69. package/dist/services/vault-resolver.d.ts +0 -8
  70. package/dist/services/vault-resolver.d.ts.map +1 -1
  71. package/dist/services/vault-resolver.js +0 -22
  72. package/dist/services/vault-resolver.js.map +1 -1
  73. package/dist/types.d.ts +19 -0
  74. package/dist/types.d.ts.map +1 -1
  75. package/package.json +7 -5
  76. package/dist/runtime/audit-logger.d.ts +0 -96
  77. package/dist/runtime/audit-logger.d.ts.map +0 -1
  78. package/dist/runtime/audit-logger.js +0 -276
  79. package/dist/runtime/audit-logger.js.map +0 -1
@@ -0,0 +1,953 @@
1
+ import { canonicalize } from "json-canonicalize";
2
+ import { CompactJwsAuditSignatureVerifier, CryptoProviderAuditHasher, createAuditTrail, parseAuditProducerEvent, parseSignedAuditEntry, } from "@kya-os/mcp/audit";
3
+ import { WebCryptoProvider } from "../providers/crypto";
4
+ import { VerifiedAuditRecorderClient } from "./verified-recorder-client";
5
+ const INTERNAL_ORIGIN = "https://audit-producer.internal";
6
+ const INTERNAL_TOKEN_HEADER = "x-kya-audit-internal-token";
7
+ const MAX_ERROR_LENGTH = 512;
8
+ const MAX_INSPECTION_LIMIT = 100;
9
+ export const AUDIT_PRODUCER_BINDING = "AUDIT_PRODUCER";
10
+ export const AUDIT_PRODUCER_CLASS_NAME = "AuditProducer";
11
+ class SourceConstructionPendingError extends Error {
12
+ constructor(sourceId) {
13
+ super(`Previous audit source event for ${sourceId} has not finished construction`);
14
+ this.name = "SourceConstructionPendingError";
15
+ }
16
+ }
17
+ class AuditProducerRequestError extends Error {
18
+ status;
19
+ constructor(status, detail) {
20
+ super(`Audit producer request failed: ${detail}`);
21
+ this.status = status;
22
+ this.name = "AuditProducerRequestError";
23
+ }
24
+ }
25
+ function boundedInteger(value, fallback, minimum, maximum) {
26
+ if (value === undefined || !/^\d+$/.test(value))
27
+ return fallback;
28
+ return Math.min(maximum, Math.max(minimum, Number(value)));
29
+ }
30
+ export function resolveAuditProducerRetryPolicy(env) {
31
+ const baseDelayMs = boundedInteger(env.KYA_OS_AUDIT_RETRY_BASE_MS, 1_000, 250, 60_000);
32
+ return {
33
+ maxAttempts: boundedInteger(env.KYA_OS_AUDIT_MAX_ATTEMPTS, 8, 1, 20),
34
+ baseDelayMs,
35
+ maxDelayMs: boundedInteger(env.KYA_OS_AUDIT_RETRY_MAX_MS, 300_000, baseDelayMs, 86_400_000),
36
+ batchSize: boundedInteger(env.KYA_OS_AUDIT_DELIVERY_BATCH_SIZE, 25, 1, 100),
37
+ deadLetterRetentionMs: boundedInteger(env.KYA_OS_AUDIT_DEAD_LETTER_RETENTION_MS, 30 * 86_400_000, 3_600_000, 90 * 86_400_000),
38
+ receiptRetentionMs: boundedInteger(env.KYA_OS_AUDIT_RECEIPT_RETENTION_MS, 30 * 86_400_000, 3_600_000, 90 * 86_400_000),
39
+ };
40
+ }
41
+ export function auditRetryDelayMs(attempts, policy) {
42
+ const exponent = Math.max(0, Math.min(30, attempts - 1));
43
+ return Math.min(policy.maxDelayMs, policy.baseDelayMs * 2 ** exponent);
44
+ }
45
+ function errorMessage(error) {
46
+ const value = error instanceof Error ? error.message : String(error);
47
+ return value.slice(0, MAX_ERROR_LENGTH);
48
+ }
49
+ function bytesToBase64(value) {
50
+ let binary = "";
51
+ for (let offset = 0; offset < value.byteLength; offset += 0x8000) {
52
+ binary += String.fromCharCode(...value.subarray(offset, offset + 0x8000));
53
+ }
54
+ return btoa(binary);
55
+ }
56
+ function base64ToBytes(value) {
57
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(value)) {
58
+ throw new TypeError("Audit evidence ciphertext is not valid base64");
59
+ }
60
+ const binary = atob(value);
61
+ return Uint8Array.from(binary, (character) => character.charCodeAt(0));
62
+ }
63
+ function toWireSubmission(submission) {
64
+ return {
65
+ ledgerId: submission.ledgerId,
66
+ ...(submission.expectedLedgerEpochId === undefined
67
+ ? {}
68
+ : { expectedLedgerEpochId: submission.expectedLedgerEpochId }),
69
+ producerEvent: submission.producerEvent,
70
+ encryptedEvidence: submission.encryptedEvidence.map((item) => ({
71
+ ref: item.ref,
72
+ ciphertextBase64: bytesToBase64(item.ciphertext),
73
+ })),
74
+ };
75
+ }
76
+ function isRecord(value) {
77
+ return typeof value === "object" && value !== null && !Array.isArray(value);
78
+ }
79
+ function parseWireSubmission(value) {
80
+ if (!isRecord(value) ||
81
+ typeof value.ledgerId !== "string" ||
82
+ value.ledgerId.trim() === "") {
83
+ throw new TypeError("Audit recorder submission ledgerId is required");
84
+ }
85
+ if (value.expectedLedgerEpochId !== undefined &&
86
+ typeof value.expectedLedgerEpochId !== "string") {
87
+ throw new TypeError("Audit recorder expectedLedgerEpochId must be a string");
88
+ }
89
+ if (!Array.isArray(value.encryptedEvidence)) {
90
+ throw new TypeError("Audit recorder encryptedEvidence must be an array");
91
+ }
92
+ const encryptedEvidence = value.encryptedEvidence.map((candidate) => {
93
+ if (!isRecord(candidate) ||
94
+ !isRecord(candidate.ref) ||
95
+ typeof candidate.ciphertextBase64 !== "string") {
96
+ throw new TypeError("Audit recorder encrypted evidence is malformed");
97
+ }
98
+ return {
99
+ ref: candidate.ref,
100
+ ciphertext: base64ToBytes(candidate.ciphertextBase64),
101
+ };
102
+ });
103
+ return {
104
+ ledgerId: value.ledgerId,
105
+ ...(value.expectedLedgerEpochId === undefined
106
+ ? {}
107
+ : { expectedLedgerEpochId: value.expectedLedgerEpochId }),
108
+ producerEvent: parseAuditProducerEvent(value.producerEvent),
109
+ encryptedEvidence,
110
+ };
111
+ }
112
+ function toWireItem(item) {
113
+ return {
114
+ eventId: item.eventId,
115
+ submission: toWireSubmission(item.submission),
116
+ enqueuedAt: item.enqueuedAt,
117
+ attempts: item.attempts,
118
+ };
119
+ }
120
+ function parseWireItem(value) {
121
+ if (!isRecord(value) ||
122
+ typeof value.eventId !== "string" ||
123
+ !Number.isFinite(value.enqueuedAt) ||
124
+ !Number.isInteger(value.attempts) ||
125
+ Number(value.attempts) < 0) {
126
+ throw new TypeError("Audit outbox item is malformed");
127
+ }
128
+ const submission = parseWireSubmission(value.submission);
129
+ if (submission.producerEvent.eventId !== value.eventId) {
130
+ throw new TypeError("Audit outbox eventId does not match its frozen event");
131
+ }
132
+ return {
133
+ eventId: value.eventId,
134
+ submission,
135
+ enqueuedAt: Number(value.enqueuedAt),
136
+ attempts: Number(value.attempts),
137
+ };
138
+ }
139
+ function parseDelivery(value) {
140
+ if (value !== "best-effort" && value !== "buffered" && value !== "required") {
141
+ throw new TypeError("Unknown audit delivery mode");
142
+ }
143
+ return value;
144
+ }
145
+ function json(value, status = 200) {
146
+ return new Response(JSON.stringify(value), {
147
+ status,
148
+ headers: { "Content-Type": "application/json; charset=utf-8" },
149
+ });
150
+ }
151
+ function problem(status, code, detail) {
152
+ return json({
153
+ type: `urn:kya-os:problem:${code.toLowerCase()}`,
154
+ title: code,
155
+ status,
156
+ detail,
157
+ }, status);
158
+ }
159
+ function sameSigner(left, right) {
160
+ return (left.did === right.did && left.kid === right.kid && left.alg === right.alg);
161
+ }
162
+ function requiredEnv(env, key) {
163
+ const value = env[key];
164
+ if (typeof value !== "string" || value.trim() === "") {
165
+ throw new Error(`Audit producer is not configured: ${String(key)} is required`);
166
+ }
167
+ return value;
168
+ }
169
+ function recorderTrustFromConfig(env) {
170
+ let config;
171
+ try {
172
+ config = JSON.parse(requiredEnv(env, "KYA_OS_AUDIT_CONFIG"));
173
+ }
174
+ catch (error) {
175
+ throw new Error(`KYA_OS_AUDIT_CONFIG is invalid: ${errorMessage(error)}`);
176
+ }
177
+ if (!isRecord(config) ||
178
+ !isRecord(config.recorderSigner) ||
179
+ !isRecord(config.recorderPublicJwk)) {
180
+ throw new Error("KYA_OS_AUDIT_CONFIG must contain recorderSigner and recorderPublicJwk");
181
+ }
182
+ const signer = config.recorderSigner;
183
+ if (typeof signer.did !== "string" ||
184
+ typeof signer.kid !== "string" ||
185
+ (signer.alg !== "EdDSA" && signer.alg !== "ES256")) {
186
+ throw new Error("KYA_OS_AUDIT_CONFIG recorderSigner is malformed");
187
+ }
188
+ if (typeof config.recorderPublicJwk.kty !== "string") {
189
+ throw new Error("KYA_OS_AUDIT_CONFIG recorderPublicJwk is malformed");
190
+ }
191
+ return {
192
+ expectedRecorder: { did: signer.did, kid: signer.kid, alg: signer.alg },
193
+ key: config.recorderPublicJwk,
194
+ };
195
+ }
196
+ function buildVerifiedRecorder(env) {
197
+ const { expectedRecorder, key } = recorderTrustFromConfig(env);
198
+ const verifier = new CompactJwsAuditSignatureVerifier({
199
+ resolve: async (signer) => sameSigner(signer, expectedRecorder) ? key : null,
200
+ });
201
+ return new VerifiedAuditRecorderClient({
202
+ endpoint: requiredEnv(env, "KYA_OS_AUDIT_RECORDER_URL"),
203
+ ...(env.KYA_OS_AUDIT_RECORDER_SERVICE === undefined
204
+ ? {}
205
+ : { transport: env.KYA_OS_AUDIT_RECORDER_SERVICE }),
206
+ ...(env.KYA_OS_AUDIT_INGEST_KEY === undefined
207
+ ? {}
208
+ : {
209
+ headers: { Authorization: `Bearer ${env.KYA_OS_AUDIT_INGEST_KEY}` },
210
+ }),
211
+ hasher: new CryptoProviderAuditHasher(new WebCryptoProvider()),
212
+ signatureVerifier: verifier,
213
+ expectedRecorder,
214
+ });
215
+ }
216
+ /** Pure delivery coordinator; persistence and alarms are injected for deterministic tests. */
217
+ export class AuditProducerDeliveryEngine {
218
+ store;
219
+ recorder;
220
+ alarms;
221
+ policy;
222
+ clock;
223
+ constructor(store, recorder, alarms, policy, clock = { now: () => Date.now() }) {
224
+ this.store = store;
225
+ this.recorder = recorder;
226
+ this.alarms = alarms;
227
+ this.policy = policy;
228
+ this.clock = clock;
229
+ }
230
+ async enqueue(item) {
231
+ const now = this.clock.now();
232
+ this.store.enqueue(item, now);
233
+ await this.alarms.setAlarm(now + 1);
234
+ }
235
+ async flush() {
236
+ let delivered = 0;
237
+ let failed = 0;
238
+ let deadLettered = 0;
239
+ const blockedSources = new Set();
240
+ this.store.prune(this.clock.now(), this.policy);
241
+ while (delivered + failed < this.policy.batchSize) {
242
+ const items = this.store.pending(this.clock.now(), this.policy.batchSize - delivered - failed, blockedSources);
243
+ if (items.length === 0)
244
+ break;
245
+ for (const item of items) {
246
+ const sourceId = item.submission.producerEvent.source.sourceId;
247
+ try {
248
+ const entry = await this.recorder().submit(item.submission);
249
+ this.store.markDelivered(item.eventId, entry, this.clock.now());
250
+ delivered += 1;
251
+ }
252
+ catch (error) {
253
+ const outcome = this.store.markFailed(item.eventId, error, this.clock.now(), this.policy);
254
+ blockedSources.add(sourceId);
255
+ failed += 1;
256
+ if (outcome.state === "dead")
257
+ deadLettered += 1;
258
+ }
259
+ if (delivered + failed >= this.policy.batchSize)
260
+ break;
261
+ }
262
+ }
263
+ await this.scheduleNext();
264
+ return { delivered, failed, deadLettered };
265
+ }
266
+ async scheduleNext() {
267
+ const now = this.clock.now();
268
+ const next = this.store.nextAlarmAt(now, this.policy);
269
+ if (next === null) {
270
+ await this.alarms.deleteAlarm();
271
+ return;
272
+ }
273
+ await this.alarms.setAlarm(Math.max(now + 1, next));
274
+ }
275
+ }
276
+ class SqliteAuditProducerStore {
277
+ state;
278
+ sql;
279
+ constructor(state) {
280
+ this.state = state;
281
+ this.sql = state.storage.sql;
282
+ }
283
+ initialize() {
284
+ this.sql.exec(`CREATE TABLE IF NOT EXISTS audit_outbox (
285
+ enqueue_order INTEGER PRIMARY KEY AUTOINCREMENT,
286
+ event_id TEXT NOT NULL UNIQUE,
287
+ source_id TEXT NOT NULL,
288
+ source_sequence TEXT,
289
+ submission_json TEXT NOT NULL,
290
+ enqueued_at INTEGER NOT NULL,
291
+ attempts INTEGER NOT NULL,
292
+ available_at INTEGER NOT NULL,
293
+ state TEXT NOT NULL CHECK (state IN ('pending', 'delivered', 'dead')),
294
+ last_error TEXT,
295
+ dead_lettered_at INTEGER,
296
+ updated_at INTEGER NOT NULL
297
+ )`);
298
+ this.sql.exec("CREATE INDEX IF NOT EXISTS audit_outbox_due ON audit_outbox(state, available_at, enqueue_order)");
299
+ this.sql.exec("CREATE INDEX IF NOT EXISTS audit_outbox_source ON audit_outbox(source_id, enqueue_order)");
300
+ this.sql.exec(`CREATE TABLE IF NOT EXISTS audit_receipts (
301
+ event_id TEXT PRIMARY KEY,
302
+ entry_json TEXT NOT NULL,
303
+ entry_digest TEXT NOT NULL,
304
+ recorded_at INTEGER NOT NULL,
305
+ received_at INTEGER NOT NULL
306
+ )`);
307
+ this.sql.exec(`CREATE TABLE IF NOT EXISTS audit_source_heads (
308
+ source_id TEXT PRIMARY KEY,
309
+ next_sequence TEXT NOT NULL,
310
+ receipted_watermark TEXT NOT NULL,
311
+ last_event_digest TEXT
312
+ )`);
313
+ this.sql.exec(`CREATE TABLE IF NOT EXISTS audit_source_claims (
314
+ source_id TEXT NOT NULL,
315
+ event_id TEXT NOT NULL,
316
+ sequence TEXT NOT NULL,
317
+ previous_event_digest TEXT,
318
+ event_digest TEXT,
319
+ receipt_digest TEXT,
320
+ PRIMARY KEY (source_id, event_id),
321
+ UNIQUE (source_id, sequence)
322
+ )`);
323
+ }
324
+ enqueue(item, now) {
325
+ const submissionJson = canonicalize(toWireSubmission(item.submission));
326
+ const existing = this.sql
327
+ .exec("SELECT submission_json FROM audit_outbox WHERE event_id = ?", item.eventId)
328
+ .toArray()[0];
329
+ if (existing !== undefined) {
330
+ if (existing.submission_json !== submissionJson) {
331
+ throw new Error(`Audit outbox event identity collision: ${item.eventId}`);
332
+ }
333
+ return;
334
+ }
335
+ this.sql.exec(`INSERT INTO audit_outbox (
336
+ event_id, source_id, source_sequence, submission_json, enqueued_at,
337
+ attempts, available_at, state, updated_at
338
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?)`, item.eventId, item.submission.producerEvent.source.sourceId, item.submission.producerEvent.source.sourceSequence ?? null, submissionJson, item.enqueuedAt, item.attempts, now, now);
339
+ }
340
+ pending(now, limit, blockedSources) {
341
+ const rows = this.sql
342
+ .exec(`SELECT current.event_id, current.submission_json, current.enqueued_at,
343
+ current.attempts, current.available_at, current.source_id
344
+ FROM audit_outbox current
345
+ WHERE current.state = 'pending' AND current.available_at <= ?
346
+ AND NOT EXISTS (
347
+ SELECT 1 FROM audit_outbox earlier
348
+ WHERE earlier.source_id = current.source_id
349
+ AND earlier.state = 'pending'
350
+ AND earlier.enqueue_order < current.enqueue_order
351
+ )
352
+ ORDER BY current.enqueue_order ASC
353
+ LIMIT 1000`, now)
354
+ .toArray();
355
+ const result = [];
356
+ for (const row of rows) {
357
+ if (blockedSources.has(row.source_id))
358
+ continue;
359
+ result.push({
360
+ eventId: row.event_id,
361
+ submission: parseWireSubmission(JSON.parse(row.submission_json)),
362
+ enqueuedAt: row.enqueued_at,
363
+ attempts: row.attempts,
364
+ availableAt: row.available_at,
365
+ });
366
+ if (result.length >= limit)
367
+ break;
368
+ }
369
+ return result;
370
+ }
371
+ saveReceipt(eventId, entry, now) {
372
+ this.sql.exec(`INSERT INTO audit_receipts (event_id, entry_json, entry_digest, recorded_at, received_at)
373
+ VALUES (?, ?, ?, ?, ?)
374
+ ON CONFLICT(event_id) DO UPDATE SET
375
+ entry_json = excluded.entry_json,
376
+ entry_digest = excluded.entry_digest,
377
+ recorded_at = excluded.recorded_at,
378
+ received_at = excluded.received_at`, eventId, canonicalize(entry), entry.entryDigest, entry.core.recordedAt, now);
379
+ }
380
+ markDelivered(eventId, entry, now) {
381
+ const outbox = this.sql
382
+ .exec("SELECT source_id, source_sequence FROM audit_outbox WHERE event_id = ?", eventId)
383
+ .toArray()[0];
384
+ this.state.storage.transactionSync(() => {
385
+ this.saveReceipt(eventId, entry, now);
386
+ this.sql.exec("UPDATE audit_outbox SET state = 'delivered', last_error = NULL, updated_at = ? WHERE event_id = ?", now, eventId);
387
+ });
388
+ // Alarm delivery bypasses AuditTrailService.flush(), so advance the same
389
+ // durable source watermark here after the authoritative receipt is saved.
390
+ if (outbox?.source_sequence !== null &&
391
+ outbox?.source_sequence !== undefined) {
392
+ try {
393
+ this.markReceipted(outbox.source_id, outbox.source_sequence, entry.entryDigest);
394
+ }
395
+ catch (error) {
396
+ // The recorder commit is authoritative. Preserve the local reconciliation
397
+ // failure without relabelling a verified delivery as failed/retryable.
398
+ this.sql.exec("UPDATE audit_outbox SET last_error = ? WHERE event_id = ?", `SOURCE_STATE: ${errorMessage(error)}`, eventId);
399
+ }
400
+ }
401
+ }
402
+ markDeliveredFromReceipt(eventId, now) {
403
+ if (this.receipt(eventId) === undefined)
404
+ return false;
405
+ this.sql.exec("UPDATE audit_outbox SET state = 'delivered', last_error = NULL, updated_at = ? WHERE event_id = ?", now, eventId);
406
+ return (this.sql
407
+ .exec("SELECT state FROM audit_outbox WHERE event_id = ?", eventId)
408
+ .toArray()[0]?.state === "delivered");
409
+ }
410
+ markFailed(eventId, error, now, policy) {
411
+ const row = this.sql
412
+ .exec("SELECT attempts, state FROM audit_outbox WHERE event_id = ?", eventId)
413
+ .toArray()[0];
414
+ if (row === undefined || row.state !== "pending") {
415
+ throw new RangeError(`Unknown pending audit outbox item: ${eventId}`);
416
+ }
417
+ const attempts = row.attempts + 1;
418
+ if (attempts >= policy.maxAttempts) {
419
+ this.sql.exec(`UPDATE audit_outbox SET attempts = ?, state = 'dead', last_error = ?,
420
+ dead_lettered_at = ?, updated_at = ? WHERE event_id = ?`, attempts, errorMessage(error), now, now, eventId);
421
+ return { state: "dead", attempts };
422
+ }
423
+ const availableAt = now + auditRetryDelayMs(attempts, policy);
424
+ this.sql.exec(`UPDATE audit_outbox SET attempts = ?, available_at = ?, last_error = ?,
425
+ updated_at = ? WHERE event_id = ?`, attempts, availableAt, errorMessage(error), now, eventId);
426
+ return { state: "pending", attempts, availableAt };
427
+ }
428
+ nextAlarmAt(_now, policy) {
429
+ const pending = this.sql
430
+ .exec("SELECT MIN(available_at) AS value FROM audit_outbox WHERE state = 'pending'")
431
+ .toArray()[0]?.value;
432
+ const dead = this.sql
433
+ .exec("SELECT MIN(dead_lettered_at) AS value FROM audit_outbox WHERE state = 'dead'")
434
+ .toArray()[0]?.value;
435
+ const receipt = this.sql
436
+ .exec("SELECT MIN(received_at) AS value FROM audit_receipts")
437
+ .toArray()[0]?.value;
438
+ const candidates = [
439
+ pending,
440
+ dead === null || dead === undefined
441
+ ? null
442
+ : dead + policy.deadLetterRetentionMs,
443
+ receipt === null || receipt === undefined
444
+ ? null
445
+ : receipt + policy.receiptRetentionMs,
446
+ ].filter((value) => value !== null && value !== undefined);
447
+ return candidates.length === 0 ? null : Math.min(...candidates);
448
+ }
449
+ prune(now, policy) {
450
+ this.state.storage.transactionSync(() => {
451
+ this.sql.exec("DELETE FROM audit_outbox WHERE state = 'dead' AND dead_lettered_at < ?", now - policy.deadLetterRetentionMs);
452
+ this.sql.exec("DELETE FROM audit_outbox WHERE state = 'delivered' AND updated_at < ?", now - policy.receiptRetentionMs);
453
+ this.sql.exec("DELETE FROM audit_receipts WHERE received_at < ?", now - policy.receiptRetentionMs);
454
+ });
455
+ }
456
+ deadLetters(limit) {
457
+ return this.sql
458
+ .exec(`SELECT event_id, source_id, source_sequence, attempts, enqueued_at,
459
+ dead_lettered_at, last_error
460
+ FROM audit_outbox WHERE state = 'dead'
461
+ ORDER BY dead_lettered_at DESC LIMIT ?`, limit)
462
+ .toArray()
463
+ .map((row) => ({
464
+ eventId: row.event_id,
465
+ sourceId: row.source_id,
466
+ ...(row.source_sequence === null
467
+ ? {}
468
+ : { sourceSequence: row.source_sequence }),
469
+ attempts: row.attempts,
470
+ enqueuedAt: row.enqueued_at,
471
+ deadLetteredAt: row.dead_lettered_at,
472
+ lastError: row.last_error,
473
+ }));
474
+ }
475
+ redrive(eventId, now) {
476
+ this.sql.exec(`UPDATE audit_outbox SET state = 'pending', attempts = 0, available_at = ?,
477
+ last_error = NULL, dead_lettered_at = NULL, updated_at = ?
478
+ WHERE event_id = ? AND state = 'dead'`, now, now, eventId);
479
+ return (this.sql
480
+ .exec("SELECT state FROM audit_outbox WHERE event_id = ?", eventId)
481
+ .toArray()[0]?.state === "pending");
482
+ }
483
+ receipt(eventId) {
484
+ const row = this.sql
485
+ .exec("SELECT entry_json FROM audit_receipts WHERE event_id = ?", eventId)
486
+ .toArray()[0];
487
+ return row === undefined
488
+ ? undefined
489
+ : parseSignedAuditEntry(JSON.parse(row.entry_json));
490
+ }
491
+ claimEvent(sourceId, eventId) {
492
+ return this.state.storage.transactionSync(() => {
493
+ const existing = this.sql
494
+ .exec("SELECT sequence, previous_event_digest FROM audit_source_claims WHERE source_id = ? AND event_id = ?", sourceId, eventId)
495
+ .toArray()[0];
496
+ if (existing !== undefined) {
497
+ return {
498
+ sequence: existing.sequence,
499
+ ...(existing.previous_event_digest === null
500
+ ? {}
501
+ : {
502
+ previousSourceEventDigest: existing.previous_event_digest,
503
+ }),
504
+ };
505
+ }
506
+ const head = this.sql
507
+ .exec("SELECT next_sequence, last_event_digest FROM audit_source_heads WHERE source_id = ?", sourceId)
508
+ .toArray()[0];
509
+ if (head !== undefined) {
510
+ const current = this.sql
511
+ .exec("SELECT event_digest FROM audit_source_claims WHERE source_id = ? AND sequence = ?", sourceId, head.next_sequence)
512
+ .toArray()[0];
513
+ if (current?.event_digest === null) {
514
+ throw new SourceConstructionPendingError(sourceId);
515
+ }
516
+ }
517
+ const sequence = (BigInt(head?.next_sequence ?? "0") + 1n).toString();
518
+ const previous = head?.last_event_digest ?? null;
519
+ this.sql.exec(`INSERT INTO audit_source_heads (source_id, next_sequence, receipted_watermark, last_event_digest)
520
+ VALUES (?, ?, '0', ?)
521
+ ON CONFLICT(source_id) DO UPDATE SET next_sequence = excluded.next_sequence`, sourceId, sequence, previous);
522
+ this.sql.exec(`INSERT INTO audit_source_claims
523
+ (source_id, event_id, sequence, previous_event_digest)
524
+ VALUES (?, ?, ?, ?)`, sourceId, eventId, sequence, previous);
525
+ return {
526
+ sequence,
527
+ ...(previous === null
528
+ ? {}
529
+ : { previousSourceEventDigest: previous }),
530
+ };
531
+ });
532
+ }
533
+ markEmitted(sourceId, eventId, sequence, eventDigest) {
534
+ this.state.storage.transactionSync(() => {
535
+ const claim = this.sql
536
+ .exec("SELECT sequence, event_digest FROM audit_source_claims WHERE source_id = ? AND event_id = ?", sourceId, eventId)
537
+ .toArray()[0];
538
+ if (claim === undefined || claim.sequence !== sequence) {
539
+ throw new RangeError("Unknown source event claim");
540
+ }
541
+ if (claim.event_digest !== null && claim.event_digest !== eventDigest) {
542
+ throw new Error(`Source event identity collision: ${eventId}`);
543
+ }
544
+ this.sql.exec("UPDATE audit_source_claims SET event_digest = ? WHERE source_id = ? AND event_id = ?", eventDigest, sourceId, eventId);
545
+ this.sql.exec("UPDATE audit_source_heads SET last_event_digest = ? WHERE source_id = ? AND next_sequence = ?", eventDigest, sourceId, sequence);
546
+ });
547
+ }
548
+ markReceipted(sourceId, sequence, entryDigest) {
549
+ this.state.storage.transactionSync(() => {
550
+ const claim = this.sql
551
+ .exec("SELECT event_id FROM audit_source_claims WHERE source_id = ? AND sequence = ?", sourceId, sequence)
552
+ .toArray()[0];
553
+ if (claim === undefined)
554
+ throw new RangeError("Unknown source sequence");
555
+ this.sql.exec("UPDATE audit_source_claims SET receipt_digest = ? WHERE source_id = ? AND sequence = ?", entryDigest, sourceId, sequence);
556
+ const head = this.sql
557
+ .exec("SELECT receipted_watermark FROM audit_source_heads WHERE source_id = ?", sourceId)
558
+ .toArray()[0];
559
+ let watermark = BigInt(head?.receipted_watermark ?? "0");
560
+ const receipted = new Set(this.sql
561
+ .exec("SELECT sequence FROM audit_source_claims WHERE source_id = ? AND receipt_digest IS NOT NULL", sourceId)
562
+ .toArray()
563
+ .map((row) => row.sequence));
564
+ while (receipted.has((watermark + 1n).toString()))
565
+ watermark += 1n;
566
+ this.sql.exec("UPDATE audit_source_heads SET receipted_watermark = ? WHERE source_id = ?", watermark.toString(), sourceId);
567
+ for (const row of this.sql
568
+ .exec("SELECT event_id, sequence FROM audit_source_claims WHERE source_id = ?", sourceId)
569
+ .toArray()) {
570
+ if (BigInt(row.sequence) < watermark) {
571
+ this.sql.exec("DELETE FROM audit_source_claims WHERE source_id = ? AND event_id = ?", sourceId, row.event_id);
572
+ }
573
+ }
574
+ });
575
+ }
576
+ getSourceState(sourceId) {
577
+ const head = this.sql
578
+ .exec("SELECT next_sequence, receipted_watermark FROM audit_source_heads WHERE source_id = ?", sourceId)
579
+ .toArray()[0];
580
+ if (head === undefined) {
581
+ return {
582
+ sourceId,
583
+ highestEmitted: "0",
584
+ highestReceipted: "0",
585
+ pendingSequences: [],
586
+ };
587
+ }
588
+ const pendingSequences = this.sql
589
+ .exec("SELECT sequence FROM audit_source_claims WHERE source_id = ? AND receipt_digest IS NULL", sourceId)
590
+ .toArray()
591
+ .map((row) => row.sequence)
592
+ .sort((left, right) => {
593
+ const a = BigInt(left);
594
+ const b = BigInt(right);
595
+ return a < b ? -1 : a > b ? 1 : 0;
596
+ });
597
+ return {
598
+ sourceId,
599
+ highestEmitted: head.next_sequence,
600
+ highestReceipted: head.receipted_watermark,
601
+ pendingSequences,
602
+ };
603
+ }
604
+ }
605
+ function constantTimeTokenEqual(expected, actual) {
606
+ if (actual === null)
607
+ return false;
608
+ const left = new TextEncoder().encode(expected);
609
+ const right = new TextEncoder().encode(actual);
610
+ let difference = left.byteLength ^ right.byteLength;
611
+ const length = Math.max(left.byteLength, right.byteLength);
612
+ for (let index = 0; index < length; index += 1) {
613
+ difference |= (left[index] ?? 0) ^ (right[index] ?? 0);
614
+ }
615
+ return difference === 0;
616
+ }
617
+ /**
618
+ * SQLite-backed producer boundary. It is deliberately separate from the MCP
619
+ * agent Durable Object, so recorder latency and retry alarms cannot mutate or
620
+ * stall authorization, consent, delegation, or session state.
621
+ */
622
+ export class AuditProducer {
623
+ state;
624
+ env;
625
+ store;
626
+ policy;
627
+ delivery;
628
+ ready;
629
+ recorderClient;
630
+ constructor(state, env) {
631
+ this.state = state;
632
+ this.env = env;
633
+ this.store = new SqliteAuditProducerStore(state);
634
+ this.policy = resolveAuditProducerRetryPolicy(env);
635
+ this.ready = state.blockConcurrencyWhile(async () => this.store.initialize());
636
+ this.delivery = new AuditProducerDeliveryEngine(this.store, () => this.recorder(), state.storage, this.policy);
637
+ }
638
+ async fetch(request) {
639
+ await this.ready;
640
+ const expectedInternalToken = this.env.KYA_OS_AUDIT_PRODUCER_INTERNAL_TOKEN;
641
+ if (typeof expectedInternalToken !== "string" ||
642
+ expectedInternalToken.trim().length === 0) {
643
+ return problem(503, "AUDIT_PRODUCER_MISCONFIGURED", "Internal audit producer authentication is not configured");
644
+ }
645
+ if (!constantTimeTokenEqual(expectedInternalToken, request.headers.get(INTERNAL_TOKEN_HEADER))) {
646
+ return problem(401, "AUDIT_PRODUCER_UNAUTHORIZED", "Invalid internal audit producer token");
647
+ }
648
+ const url = new URL(request.url);
649
+ try {
650
+ if (request.method === "POST" && url.pathname === "/v1/record") {
651
+ const body = await request.json();
652
+ if (!isRecord(body))
653
+ throw new TypeError("Audit producer request must be an object");
654
+ parseDelivery(body.delivery);
655
+ const submission = parseWireSubmission(body.submission);
656
+ const entry = await this.recorder().submit(submission);
657
+ this.store.saveReceipt(submission.producerEvent.eventId, entry, Date.now());
658
+ return json(entry);
659
+ }
660
+ if (request.method === "POST" && url.pathname === "/v1/outbox") {
661
+ const body = await request.json();
662
+ if (!isRecord(body) || parseDelivery(body.delivery) !== "buffered") {
663
+ throw new TypeError("Audit outbox requires buffered delivery");
664
+ }
665
+ await this.delivery.enqueue(parseWireItem(body.item));
666
+ return json({ status: "pending" }, 202);
667
+ }
668
+ if (request.method === "GET" && url.pathname === "/v1/outbox/pending") {
669
+ const limit = boundedInteger(url.searchParams.get("limit") ?? undefined, 100, 1, 100);
670
+ const items = this.store.pending(Date.now(), limit, new Set());
671
+ return json({ items: items.map(toWireItem) });
672
+ }
673
+ if (request.method === "POST" && url.pathname === "/v1/outbox/flush") {
674
+ return json(await this.delivery.flush());
675
+ }
676
+ if (request.method === "POST" &&
677
+ url.pathname === "/v1/outbox/delivered") {
678
+ const body = await request.json();
679
+ if (!isRecord(body) || typeof body.eventId !== "string") {
680
+ throw new TypeError("eventId is required");
681
+ }
682
+ if (!this.store.markDeliveredFromReceipt(body.eventId, Date.now())) {
683
+ return problem(409, "AUDIT_RECEIPT_MISSING", "A verified receipt is required before delivery can complete");
684
+ }
685
+ await this.delivery.scheduleNext();
686
+ return json({ status: "delivered" });
687
+ }
688
+ if (request.method === "POST" && url.pathname === "/v1/outbox/failed") {
689
+ const body = await request.json();
690
+ if (!isRecord(body) || typeof body.eventId !== "string") {
691
+ throw new TypeError("eventId is required");
692
+ }
693
+ const outcome = this.store.markFailed(body.eventId, typeof body.error === "string" ? body.error : "Audit delivery failed", Date.now(), this.policy);
694
+ await this.delivery.scheduleNext();
695
+ return json(outcome);
696
+ }
697
+ if (request.method === "GET" && url.pathname === "/v1/dead-letters") {
698
+ const limit = boundedInteger(url.searchParams.get("limit") ?? undefined, 25, 1, MAX_INSPECTION_LIMIT);
699
+ return json({ deadLetters: this.store.deadLetters(limit) });
700
+ }
701
+ const redrive = url.pathname.match(/^\/v1\/dead-letters\/([^/]+)\/redrive$/);
702
+ if (request.method === "POST" && redrive !== null) {
703
+ const eventId = decodeURIComponent(redrive[1]);
704
+ if (!this.store.redrive(eventId, Date.now())) {
705
+ return problem(404, "AUDIT_DEAD_LETTER_NOT_FOUND", "Dead-lettered event was not found");
706
+ }
707
+ await this.delivery.scheduleNext();
708
+ return json({ status: "pending", eventId });
709
+ }
710
+ const receipt = url.pathname.match(/^\/v1\/receipts\/([^/]+)$/);
711
+ if (request.method === "GET" && receipt !== null) {
712
+ const eventId = decodeURIComponent(receipt[1]);
713
+ const entry = this.store.receipt(eventId);
714
+ return entry === undefined
715
+ ? problem(404, "AUDIT_RECEIPT_NOT_FOUND", "Verified receipt was not found")
716
+ : json(entry);
717
+ }
718
+ if (request.method === "POST" && url.pathname === "/v1/source/claim") {
719
+ const body = await request.json();
720
+ if (!isRecord(body) ||
721
+ typeof body.sourceId !== "string" ||
722
+ typeof body.eventId !== "string") {
723
+ throw new TypeError("sourceId and eventId are required");
724
+ }
725
+ try {
726
+ return json(this.store.claimEvent(body.sourceId, body.eventId));
727
+ }
728
+ catch (error) {
729
+ if (error instanceof SourceConstructionPendingError) {
730
+ return problem(409, "AUDIT_SOURCE_CONSTRUCTION_PENDING", error.message);
731
+ }
732
+ throw error;
733
+ }
734
+ }
735
+ if (request.method === "POST" && url.pathname === "/v1/source/emitted") {
736
+ const body = await request.json();
737
+ if (!isRecord(body) ||
738
+ typeof body.sourceId !== "string" ||
739
+ typeof body.eventId !== "string" ||
740
+ typeof body.sequence !== "string" ||
741
+ typeof body.eventDigest !== "string") {
742
+ throw new TypeError("Source emission is malformed");
743
+ }
744
+ this.store.markEmitted(body.sourceId, body.eventId, body.sequence, body.eventDigest);
745
+ return json({ status: "emitted" });
746
+ }
747
+ if (request.method === "POST" &&
748
+ url.pathname === "/v1/source/receipted") {
749
+ const body = await request.json();
750
+ if (!isRecord(body) ||
751
+ typeof body.sourceId !== "string" ||
752
+ typeof body.sequence !== "string" ||
753
+ typeof body.entryDigest !== "string") {
754
+ throw new TypeError("Source receipt is malformed");
755
+ }
756
+ this.store.markReceipted(body.sourceId, body.sequence, body.entryDigest);
757
+ return json({ status: "receipted" });
758
+ }
759
+ if (request.method === "GET" && url.pathname === "/v1/source") {
760
+ const sourceId = url.searchParams.get("sourceId");
761
+ if (sourceId === null || sourceId.trim() === "") {
762
+ throw new TypeError("sourceId is required");
763
+ }
764
+ return json(this.store.getSourceState(sourceId));
765
+ }
766
+ return problem(404, "AUDIT_PRODUCER_ROUTE_NOT_FOUND", "Audit producer route was not found");
767
+ }
768
+ catch (error) {
769
+ return problem(400, "AUDIT_PRODUCER_INVALID_REQUEST", errorMessage(error));
770
+ }
771
+ }
772
+ async alarm() {
773
+ await this.ready;
774
+ await this.delivery.flush();
775
+ }
776
+ recorder() {
777
+ this.recorderClient ??= buildVerifiedRecorder(this.env);
778
+ return this.recorderClient;
779
+ }
780
+ }
781
+ /** Client-side port used by AuditTrailService in the MCP worker. */
782
+ export class CloudflareAuditProducerBinding {
783
+ options;
784
+ capabilities = {
785
+ durability: "durable",
786
+ fifoPerSource: true,
787
+ atomicClaim: true,
788
+ };
789
+ stub;
790
+ constructor(options) {
791
+ this.options = options;
792
+ if (options.instanceName.trim() === "") {
793
+ throw new TypeError("Audit producer instanceName must be non-empty");
794
+ }
795
+ const id = options.namespace.idFromName(options.instanceName);
796
+ this.stub = options.namespace.get(id);
797
+ }
798
+ async submit(input) {
799
+ const response = await this.request("/v1/record", {
800
+ method: "POST",
801
+ body: JSON.stringify({
802
+ delivery: this.options.delivery,
803
+ submission: toWireSubmission(input),
804
+ }),
805
+ });
806
+ return parseSignedAuditEntry(await response.json());
807
+ }
808
+ async enqueue(item) {
809
+ await this.request("/v1/outbox", {
810
+ method: "POST",
811
+ body: JSON.stringify({ delivery: "buffered", item: toWireItem(item) }),
812
+ });
813
+ }
814
+ async *pending(limit = 100) {
815
+ const response = await this.request(`/v1/outbox/pending?limit=${Math.max(1, Math.min(100, limit))}`);
816
+ const body = await response.json();
817
+ if (!isRecord(body) || !Array.isArray(body.items)) {
818
+ throw new Error("Audit producer returned a malformed pending response");
819
+ }
820
+ for (const item of body.items)
821
+ yield parseWireItem(item);
822
+ }
823
+ async markDelivered(eventId) {
824
+ await this.request("/v1/outbox/delivered", {
825
+ method: "POST",
826
+ body: JSON.stringify({ eventId }),
827
+ });
828
+ }
829
+ async markFailed(eventId, error) {
830
+ await this.request("/v1/outbox/failed", {
831
+ method: "POST",
832
+ body: JSON.stringify({ eventId, error: errorMessage(error) }),
833
+ });
834
+ }
835
+ async claimEvent(sourceId, eventId) {
836
+ for (let attempt = 0;; attempt += 1) {
837
+ try {
838
+ const response = await this.request("/v1/source/claim", {
839
+ method: "POST",
840
+ body: JSON.stringify({ sourceId, eventId }),
841
+ });
842
+ return (await response.json());
843
+ }
844
+ catch (error) {
845
+ if (!(error instanceof AuditProducerRequestError) ||
846
+ error.status !== 409 ||
847
+ attempt >= 9) {
848
+ throw error;
849
+ }
850
+ await new Promise((resolve) => setTimeout(resolve, Math.min(50, 5 * 2 ** attempt)));
851
+ }
852
+ }
853
+ }
854
+ async markEmitted(sourceId, eventId, sequence, eventDigest) {
855
+ await this.request("/v1/source/emitted", {
856
+ method: "POST",
857
+ body: JSON.stringify({ sourceId, eventId, sequence, eventDigest }),
858
+ });
859
+ }
860
+ async markReceipted(sourceId, sequence, entryDigest) {
861
+ await this.request("/v1/source/receipted", {
862
+ method: "POST",
863
+ body: JSON.stringify({ sourceId, sequence, entryDigest }),
864
+ });
865
+ }
866
+ async getState(sourceId) {
867
+ const response = await this.request(`/v1/source?sourceId=${encodeURIComponent(sourceId)}`);
868
+ return (await response.json());
869
+ }
870
+ /** Return only a locally verified recorder entry retained by the producer. */
871
+ async receipt(eventId) {
872
+ try {
873
+ const response = await this.request(`/v1/receipts/${encodeURIComponent(eventId)}`);
874
+ return parseSignedAuditEntry(await response.json());
875
+ }
876
+ catch (error) {
877
+ if (error instanceof AuditProducerRequestError && error.status === 404)
878
+ return undefined;
879
+ throw error;
880
+ }
881
+ }
882
+ async request(path, init = {}) {
883
+ const requestHeaders = new Headers(init.headers);
884
+ if (init.body !== undefined)
885
+ requestHeaders.set("Content-Type", "application/json");
886
+ if (this.options.internalToken !== undefined) {
887
+ requestHeaders.set(INTERNAL_TOKEN_HEADER, this.options.internalToken);
888
+ }
889
+ const response = await this.stub.fetch(`${INTERNAL_ORIGIN}${path}`, {
890
+ ...init,
891
+ headers: requestHeaders,
892
+ });
893
+ if (!response.ok) {
894
+ let detail = `HTTP ${response.status}`;
895
+ try {
896
+ const body = await response.json();
897
+ if (isRecord(body) && typeof body.detail === "string")
898
+ detail = body.detail;
899
+ }
900
+ catch {
901
+ // Preserve the status-only detail for malformed error responses.
902
+ }
903
+ throw new AuditProducerRequestError(response.status, detail);
904
+ }
905
+ return response;
906
+ }
907
+ }
908
+ /** Compose the core audit trail with durable Cloudflare producer mechanics. */
909
+ export function createCloudflareAuditTrail(options) {
910
+ if (options.env.AUDIT_PRODUCER === undefined) {
911
+ throw new Error("AUDIT_PRODUCER Durable Object binding is required for Cloudflare audit");
912
+ }
913
+ const internalToken = options.env.KYA_OS_AUDIT_PRODUCER_INTERNAL_TOKEN;
914
+ if (typeof internalToken !== "string" || internalToken.trim().length === 0) {
915
+ throw new Error("KYA_OS_AUDIT_PRODUCER_INTERNAL_TOKEN is required for Cloudflare audit");
916
+ }
917
+ const producer = new CloudflareAuditProducerBinding({
918
+ namespace: options.env.AUDIT_PRODUCER,
919
+ instanceName: options.instanceName ?? `${options.ledgerId}\0${options.sourceId}`,
920
+ delivery: options.delivery,
921
+ internalToken,
922
+ });
923
+ return createAuditTrail({
924
+ recorder: producer,
925
+ delivery: options.delivery,
926
+ hasher: options.hasher ?? new CryptoProviderAuditHasher(new WebCryptoProvider()),
927
+ ledgerId: options.ledgerId,
928
+ ...(options.expectedLedgerEpochId === undefined
929
+ ? {}
930
+ : { expectedLedgerEpochId: options.expectedLedgerEpochId }),
931
+ tenantRef: options.tenantRef,
932
+ producer: options.producer,
933
+ sourceId: options.sourceId,
934
+ binding: options.binding,
935
+ privacy: options.privacy,
936
+ clock: options.clock ?? { now: () => Date.now() },
937
+ sourceState: producer,
938
+ ...(options.delivery === "buffered" ? { outbox: producer } : {}),
939
+ ...(options.eventIdFactory === undefined
940
+ ? {}
941
+ : { eventIdFactory: options.eventIdFactory }),
942
+ ...(options.onDeliveryFailure === undefined
943
+ ? {}
944
+ : { onDeliveryFailure: options.onDeliveryFailure }),
945
+ ...(options.onSourceStateFailure === undefined
946
+ ? {}
947
+ : { onSourceStateFailure: options.onSourceStateFailure }),
948
+ ...(options.capabilities === undefined
949
+ ? {}
950
+ : { capabilities: options.capabilities }),
951
+ });
952
+ }
953
+ //# sourceMappingURL=durable-producer.js.map