mcp-event-intelligence 0.5.0 → 0.6.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 (61) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +31 -38
  3. package/SECURITY.md +4 -4
  4. package/bin/mcp-event-intelligence.mjs +2 -2
  5. package/dist/src/composite/engine.d.ts +11 -7
  6. package/dist/src/composite/engine.js +150 -414
  7. package/dist/src/composite/matching.d.ts +1 -5
  8. package/dist/src/composite/matching.js +0 -54
  9. package/dist/src/composite/runtimeSupport.js +10 -6
  10. package/dist/src/composite/store.d.ts +38 -4
  11. package/dist/src/composite/temporal.d.ts +0 -17
  12. package/dist/src/composite/temporal.js +0 -266
  13. package/dist/src/intelligenceProtocol/activationSchemas.d.ts +36 -3
  14. package/dist/src/intelligenceProtocol/activationSchemas.js +4 -3
  15. package/dist/src/intelligenceProtocol/index.d.ts +8 -12
  16. package/dist/src/intelligenceProtocol/index.js +4 -7
  17. package/dist/src/intelligenceProtocol/lifecycle.js +2 -10
  18. package/dist/src/intelligenceProtocol/operatorRegistry.d.ts +3 -270
  19. package/dist/src/intelligenceProtocol/operatorRegistry.js +5 -144
  20. package/dist/src/intelligenceProtocol/schemas.d.ts +0 -189
  21. package/dist/src/intelligenceProtocol/schemas.js +1 -97
  22. package/dist/src/intelligenceProtocol/triggerPlanSchemas.d.ts +5 -102
  23. package/dist/src/intelligenceProtocol/triggerPlanSchemas.js +2 -26
  24. package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +14 -215
  25. package/dist/src/intelligenceProtocol/triggerSchemas.js +18 -191
  26. package/dist/src/intelligenceProtocol/version.d.ts +2 -2
  27. package/dist/src/intelligenceProtocol/version.js +2 -2
  28. package/dist/src/patternV2/evaluator.d.ts +9 -1
  29. package/dist/src/patternV2/evaluator.js +63 -4
  30. package/dist/src/semantic/types.d.ts +6 -0
  31. package/dist/src/semantic/typesafeJevEvaluator.d.ts +1 -0
  32. package/dist/src/semantic/typesafeJevEvaluator.js +6 -0
  33. package/package.json +5 -2
  34. package/scripts/host-integration.d.mts +48 -5
  35. package/scripts/host-integration.mjs +16 -4
  36. package/scripts/lib/activation-hydrator.mjs +12 -12
  37. package/scripts/lib/composite-event-consumer.mjs +8 -28
  38. package/scripts/lib/composite-wake-coordinator.mjs +19 -19
  39. package/scripts/lib/derived-contract-registry.mjs +4 -4
  40. package/scripts/lib/derived-event-coordinator.mjs +11 -22
  41. package/scripts/lib/local-event-intelligence-runtime.mjs +35 -3
  42. package/scripts/lib/mcp-events-client.mjs +36 -32
  43. package/scripts/lib/persistent-event-store.mjs +228 -61
  44. package/scripts/lib/store-capabilities.mjs +88 -0
  45. package/scripts/lib/store-conformance.mjs +127 -0
  46. package/scripts/lib/temporal-deadline-scheduler.mjs +3 -3
  47. package/scripts/lib/trigger-control-plane.mjs +52 -57
  48. package/scripts/lib/trigger-inspector.mjs +48 -160
  49. package/scripts/lib/trigger-planner.mjs +37 -126
  50. package/scripts/lib/wake-retry-scheduler.mjs +2 -19
  51. package/scripts/mcp-stdio-server.mjs +8 -8
  52. package/scripts/service.mjs +19 -138
  53. package/server.json +3 -3
  54. package/dist/src/intelligenceProtocol/invariants.d.ts +0 -3
  55. package/dist/src/intelligenceProtocol/invariants.js +0 -36
  56. package/dist/src/intelligenceProtocol/mapper.d.ts +0 -14
  57. package/dist/src/intelligenceProtocol/mapper.js +0 -28
  58. package/dist/src/patternV2/legacyCompiler.d.ts +0 -3
  59. package/dist/src/patternV2/legacyCompiler.js +0 -184
  60. package/scripts/lib/event-processor.mjs +0 -624
  61. package/scripts/lib/http-wake-deliverer.mjs +0 -31
@@ -1,624 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import {
3
- assertDecisionLineage,
4
- assertWakeLineage,
5
- mapMcpEventToLineage,
6
- parseEventIngestRequest,
7
- SemanticDecisionRecordSchema,
8
- WakeRecordSchema,
9
- sha256Hex,
10
- } from '../../dist/src/intelligenceProtocol/index.js';
11
- import {
12
- SemanticConditionEngine,
13
- } from '../../dist/src/semantic/conditionEngine.js';
14
-
15
- function nowIso(now) {
16
- return now().toISOString();
17
- }
18
-
19
- async function stableId(prefix, value) {
20
- const hash = await sha256Hex(value);
21
- return `${prefix}_${hash.slice(0, 24)}`;
22
- }
23
-
24
- function retryDelayMs(attemptCount, baseDelayMs, maxDelayMs) {
25
- return Math.min(
26
- maxDelayMs,
27
- baseDelayMs * (2 ** Math.max(0, attemptCount - 1)),
28
- );
29
- }
30
-
31
- export class EventProcessor {
32
- constructor({
33
- store,
34
- evaluator = null,
35
- wakeDeliverer = null,
36
- now = () => new Date(),
37
- workerId = `event-wake-worker:${randomUUID()}`,
38
- leaseMs = 30000,
39
- maxAttempts = 5,
40
- retryBaseDelayMs = 1000,
41
- retryMaxDelayMs = 60000,
42
- }) {
43
- this.store = store;
44
- this.evaluator = evaluator;
45
- this.wakeDeliverer = wakeDeliverer;
46
- this.now = now;
47
- this.workerId = workerId;
48
- this.leaseMs = Math.max(1000, Number(leaseMs) || 30000);
49
- this.maxAttempts = Math.max(1, Number(maxAttempts) || 5);
50
- this.retryBaseDelayMs = Math.max(1, Number(retryBaseDelayMs) || 1000);
51
- this.retryMaxDelayMs = Math.max(
52
- this.retryBaseDelayMs,
53
- Number(retryMaxDelayMs) || 60000,
54
- );
55
- }
56
-
57
- async ingest(rawRequest) {
58
- const request = parseEventIngestRequest(rawRequest);
59
- const traceId =
60
- request.context.traceId ??
61
- (await stableId(
62
- 'trace',
63
- [
64
- request.context.environmentId,
65
- request.context.subscriptionId,
66
- request.event.eventId,
67
- ].join(':'),
68
- ));
69
-
70
- const lineage = await mapMcpEventToLineage({
71
- event: request.event,
72
- traceId,
73
- environmentId: request.context.environmentId,
74
- subscriptionId: request.context.subscriptionId,
75
- serverId: request.context.serverId,
76
- transport: request.context.transport,
77
- provider: request.context.provider,
78
- target: request.context.target,
79
- observedAt: nowIso(this.now),
80
- });
81
-
82
- const accepted = await this.store.registerEvent({
83
- lineage,
84
- event: request.event,
85
- });
86
-
87
- if (!accepted) {
88
- await this.store.appendAudit({
89
- auditId: await stableId('audit', `${traceId}:duplicate:${this.store.auditLength()}`),
90
- traceId,
91
- timestamp: nowIso(this.now),
92
- kind: 'event.duplicate',
93
- entityType: 'event',
94
- entityId: request.event.eventId,
95
- toState: 'duplicate',
96
- details: {
97
- subscriptionId: request.context.subscriptionId,
98
- },
99
- });
100
-
101
- return {
102
- traceId,
103
- sourceEventId: request.event.eventId,
104
- status: 'duplicate',
105
- wake: null,
106
- };
107
- }
108
-
109
- await this.store.appendAudit({
110
- auditId: await stableId('audit', `${traceId}:received`),
111
- traceId,
112
- timestamp: nowIso(this.now),
113
- kind: 'event.received',
114
- entityType: 'event',
115
- entityId: request.event.eventId,
116
- toState: 'received',
117
- details: {
118
- eventName: request.event.name,
119
- payloadHash: lineage.event.payloadHash,
120
- },
121
- });
122
-
123
- let state = 'received';
124
- let decision = null;
125
-
126
- if (request.semanticCondition) {
127
- if (!this.evaluator) {
128
- await this.store.appendAudit({
129
- auditId: await stableId('audit', `${traceId}:semantic-unavailable`),
130
- traceId,
131
- timestamp: nowIso(this.now),
132
- kind: 'error',
133
- entityType: 'event',
134
- entityId: request.event.eventId,
135
- fromState: state,
136
- toState: 'failed',
137
- details: { code: 'SEMANTIC_EVALUATOR_UNAVAILABLE' },
138
- });
139
- throw new Error('Semantic condition supplied but no evaluator is configured');
140
- }
141
-
142
- await this.store.appendAudit({
143
- auditId: await stableId('audit', `${traceId}:evaluating`),
144
- traceId,
145
- timestamp: nowIso(this.now),
146
- kind: 'lifecycle.transition',
147
- entityType: 'event',
148
- entityId: request.event.eventId,
149
- fromState: state,
150
- toState: 'evaluating',
151
- });
152
- state = 'evaluating';
153
-
154
- try {
155
- const engine = new SemanticConditionEngine(this.evaluator);
156
- const result = await engine.evaluate(
157
- { data: request.event.data },
158
- request.semanticCondition,
159
- );
160
-
161
- decision = SemanticDecisionRecordSchema.parse({
162
- decisionId: await stableId('decision', `${traceId}:semantic`),
163
- traceId,
164
- subscriptionId: request.context.subscriptionId,
165
- sourceEventId: request.event.eventId,
166
- createdAt: nowIso(this.now),
167
- evaluator: result.evaluator,
168
- outcome: result.outcome,
169
- probability: result.probability,
170
- matched: result.matched,
171
- shouldEscalate: result.shouldEscalate,
172
- policy: {
173
- matchThreshold: request.semanticCondition.matchThreshold,
174
- rejectThreshold: request.semanticCondition.rejectThreshold,
175
- uncertain: request.semanticCondition.uncertain,
176
- },
177
- inputFields: request.semanticCondition.input,
178
- providerEvidence: result.metadata
179
- ? {
180
- requestedModel:
181
- typeof result.metadata.requestedModel === 'string'
182
- ? result.metadata.requestedModel
183
- : undefined,
184
- resolvedModel:
185
- typeof result.metadata.resolvedModel === 'string'
186
- ? result.metadata.resolvedModel
187
- : undefined,
188
- requestId:
189
- typeof result.metadata.requestId === 'string'
190
- ? result.metadata.requestId
191
- : undefined,
192
- httpStatus:
193
- typeof result.metadata.httpStatus === 'number'
194
- ? result.metadata.httpStatus
195
- : undefined,
196
- inputTokens:
197
- typeof result.metadata.inputTokens === 'number'
198
- ? result.metadata.inputTokens
199
- : undefined,
200
- outputTokens:
201
- typeof result.metadata.outputTokens === 'number'
202
- ? result.metadata.outputTokens
203
- : undefined,
204
- providerReportedCost:
205
- typeof result.metadata.cost === 'number'
206
- ? result.metadata.cost
207
- : undefined,
208
- }
209
- : undefined,
210
- });
211
-
212
- assertDecisionLineage(lineage, decision);
213
- await this.store.appendDecision(decision);
214
- await this.store.appendAudit({
215
- auditId: await stableId('audit', `${traceId}:decision`),
216
- traceId,
217
- timestamp: nowIso(this.now),
218
- kind: 'decision.evaluated',
219
- entityType: 'decision',
220
- entityId: decision.decisionId,
221
- details: {
222
- evaluator: decision.evaluator,
223
- outcome: decision.outcome,
224
- probability: decision.probability,
225
- inputFields: decision.inputFields,
226
- providerEvidence: decision.providerEvidence ?? null,
227
- },
228
- });
229
-
230
- const nextState =
231
- result.outcome === 'match'
232
- ? 'matched'
233
- : result.outcome === 'reject'
234
- ? 'rejected'
235
- : 'escalated';
236
-
237
- await this.store.appendAudit({
238
- auditId: await stableId('audit', `${traceId}:${nextState}`),
239
- traceId,
240
- timestamp: nowIso(this.now),
241
- kind: 'lifecycle.transition',
242
- entityType: 'event',
243
- entityId: request.event.eventId,
244
- fromState: state,
245
- toState: nextState,
246
- });
247
- state = nextState;
248
-
249
- if (state === 'rejected' || (state === 'escalated' && !request.wakeOnEscalation)) {
250
- return {
251
- traceId,
252
- sourceEventId: request.event.eventId,
253
- status: state,
254
- decision,
255
- wake: null,
256
- };
257
- }
258
- } catch (error) {
259
- if (state === 'evaluating') {
260
- await this.store.appendAudit({
261
- auditId: await stableId('audit', `${traceId}:evaluation-failed`),
262
- traceId,
263
- timestamp: nowIso(this.now),
264
- kind: 'error',
265
- entityType: 'event',
266
- entityId: request.event.eventId,
267
- fromState: state,
268
- toState: 'failed',
269
- details: {
270
- message: error instanceof Error ? error.message : 'semantic evaluation failed',
271
- },
272
- });
273
- }
274
- throw error;
275
- }
276
- } else {
277
- await this.store.appendAudit({
278
- auditId: await stableId('audit', `${traceId}:direct-match`),
279
- traceId,
280
- timestamp: nowIso(this.now),
281
- kind: 'lifecycle.transition',
282
- entityType: 'event',
283
- entityId: request.event.eventId,
284
- fromState: state,
285
- toState: 'matched',
286
- details: { reason: 'no_semantic_condition' },
287
- });
288
- state = 'matched';
289
- }
290
-
291
- const wakeId = await stableId('wake', `${traceId}:${request.context.target.id}`);
292
- const queuedWake = WakeRecordSchema.parse({
293
- wakeId,
294
- traceId,
295
- decisionId: decision?.decisionId ?? null,
296
- subscriptionId: request.context.subscriptionId,
297
- sourceEventId: request.event.eventId,
298
- createdAt: nowIso(this.now),
299
- target: request.context.target,
300
- status: 'queued',
301
- });
302
-
303
- assertWakeLineage(lineage, decision, queuedWake);
304
- await this.store.appendWake(queuedWake);
305
- await this.store.appendAudit({
306
- auditId: await stableId('audit', `${traceId}:wake-queued`),
307
- traceId,
308
- timestamp: nowIso(this.now),
309
- kind: 'wake.queued',
310
- entityType: 'wake',
311
- entityId: wakeId,
312
- fromState: state,
313
- toState: 'wake_queued',
314
- details: {
315
- runtime: queuedWake.target.runtime,
316
- targetKind: queuedWake.target.kind,
317
- targetId: queuedWake.target.id,
318
- },
319
- });
320
-
321
- if (!this.wakeDeliverer) {
322
- return {
323
- traceId,
324
- sourceEventId: request.event.eventId,
325
- status: 'wake_queued',
326
- decision,
327
- wake: queuedWake,
328
- };
329
- }
330
-
331
- const deliveryResult = await this.deliverQueuedWake({
332
- wake: queuedWake,
333
- lineage,
334
- event: request.event,
335
- decision,
336
- });
337
-
338
- return {
339
- traceId,
340
- sourceEventId: request.event.eventId,
341
- status: deliveryResult.status,
342
- decision,
343
- wake: deliveryResult.wake,
344
- };
345
- }
346
-
347
- async deliveredWakeFromState(wake, delivery) {
348
- const latest = this.store.latestWake(wake.wakeId);
349
- if (latest?.status === 'handled' || latest?.status === 'delivered') {
350
- return latest;
351
- }
352
-
353
- const delivered = WakeRecordSchema.parse({
354
- ...wake,
355
- createdAt: nowIso(this.now),
356
- status: 'delivered',
357
- runtimeReceiptId: delivery.runtimeReceiptId ?? undefined,
358
- });
359
- await this.store.appendWake(delivered);
360
- await this.store.appendAudit({
361
- auditId: await stableId(
362
- 'audit',
363
- `${wake.traceId}:wake-delivered-recovered:${this.store.auditLength()}`,
364
- ),
365
- traceId: wake.traceId,
366
- timestamp: nowIso(this.now),
367
- kind: 'wake.delivered',
368
- entityType: 'wake',
369
- entityId: wake.wakeId,
370
- fromState: 'wake_queued',
371
- toState: 'wake_delivered',
372
- details: {
373
- runtimeReceiptId: delivered.runtimeReceiptId ?? null,
374
- recoveredFromDeliveryState: true,
375
- },
376
- });
377
- return delivered;
378
- }
379
-
380
- async deliverQueuedWake({ wake, lineage, event, decision }) {
381
- if (!this.wakeDeliverer) {
382
- return { status: 'wake_queued', wake };
383
- }
384
-
385
- const now = nowIso(this.now);
386
- await this.store.ensureWakeDelivery({
387
- wakeId: wake.wakeId,
388
- sourceType: 'event',
389
- sourceId: wake.traceId,
390
- runtime: wake.target.runtime,
391
- now,
392
- });
393
-
394
- const current = this.store.getWakeDelivery(wake.wakeId);
395
- if (current?.status === 'delivered') {
396
- return {
397
- status: 'wake_delivered',
398
- wake: await this.deliveredWakeFromState(wake, current),
399
- };
400
- }
401
- if (current?.status === 'dead_letter') {
402
- return {
403
- status: 'dead_letter',
404
- wake: this.store.latestWake(wake.wakeId) ?? wake,
405
- };
406
- }
407
-
408
- const claim = await this.store.claimWakeDelivery(wake.wakeId, {
409
- workerId: this.workerId,
410
- now,
411
- leaseMs: this.leaseMs,
412
- });
413
- if (!claim) {
414
- const delivery = this.store.getWakeDelivery(wake.wakeId);
415
- return {
416
- status:
417
- delivery?.status === 'claimed'
418
- ? 'delivery_in_progress'
419
- : 'retry_scheduled',
420
- wake: this.store.latestWake(wake.wakeId) ?? wake,
421
- };
422
- }
423
-
424
- try {
425
- const receipt = await this.wakeDeliverer({
426
- wake,
427
- lineage,
428
- event,
429
- decision,
430
- });
431
- if (
432
- !receipt ||
433
- typeof receipt.runtimeReceiptId !== 'string' ||
434
- !receipt.runtimeReceiptId
435
- ) {
436
- throw new Error('Wake deliverer must return runtimeReceiptId');
437
- }
438
-
439
- const delivery = await this.store.completeWakeDelivery(wake.wakeId, {
440
- workerId: this.workerId,
441
- runtimeReceiptId: receipt.runtimeReceiptId,
442
- now: nowIso(this.now),
443
- });
444
- const deliveredWake = WakeRecordSchema.parse({
445
- ...wake,
446
- createdAt: nowIso(this.now),
447
- status: 'delivered',
448
- runtimeReceiptId: receipt.runtimeReceiptId,
449
- });
450
- await this.store.appendWake(deliveredWake);
451
- await this.store.appendAudit({
452
- auditId: await stableId(
453
- 'audit',
454
- `${wake.traceId}:wake-delivered:${this.store.auditLength()}`,
455
- ),
456
- traceId: wake.traceId,
457
- timestamp: nowIso(this.now),
458
- kind: 'wake.delivered',
459
- entityType: 'wake',
460
- entityId: wake.wakeId,
461
- fromState: 'wake_queued',
462
- toState: 'wake_delivered',
463
- details: {
464
- runtimeReceiptId: deliveredWake.runtimeReceiptId ?? null,
465
- attemptCount: delivery?.attemptCount ?? claim.attemptCount,
466
- },
467
- });
468
- return {
469
- status: 'wake_delivered',
470
- wake: deliveredWake,
471
- };
472
- } catch (error) {
473
- const failedAt = this.now();
474
- const delay = retryDelayMs(
475
- claim.attemptCount,
476
- this.retryBaseDelayMs,
477
- this.retryMaxDelayMs,
478
- );
479
- const delivery = await this.store.failWakeDelivery(wake.wakeId, {
480
- workerId: this.workerId,
481
- error,
482
- now: failedAt.toISOString(),
483
- maxAttempts: this.maxAttempts,
484
- nextAttemptAt: new Date(failedAt.getTime() + delay).toISOString(),
485
- });
486
-
487
- if (delivery?.status !== 'dead_letter') {
488
- await this.store.appendAudit({
489
- auditId: await stableId(
490
- 'audit',
491
- `${wake.traceId}:wake-retry:${delivery?.attemptCount ?? claim.attemptCount}:${this.store.auditLength()}`,
492
- ),
493
- traceId: wake.traceId,
494
- timestamp: nowIso(this.now),
495
- kind: 'wake.retry_scheduled',
496
- entityType: 'wake',
497
- entityId: wake.wakeId,
498
- details: {
499
- attemptCount: delivery?.attemptCount ?? claim.attemptCount,
500
- nextAttemptAt: delivery?.nextAttemptAt ?? null,
501
- message:
502
- error instanceof Error
503
- ? error.message
504
- : 'wake delivery failed',
505
- },
506
- });
507
- return {
508
- status: 'retry_scheduled',
509
- wake: this.store.latestWake(wake.wakeId) ?? wake,
510
- };
511
- }
512
-
513
- const deadLetter = WakeRecordSchema.parse({
514
- ...wake,
515
- createdAt: nowIso(this.now),
516
- status: 'dead_letter',
517
- });
518
- await this.store.appendWake(deadLetter);
519
- await this.store.appendAudit({
520
- auditId: await stableId(
521
- 'audit',
522
- `${wake.traceId}:wake-dead-letter:${this.store.auditLength()}`,
523
- ),
524
- traceId: wake.traceId,
525
- timestamp: nowIso(this.now),
526
- kind: 'wake.dead_letter',
527
- entityType: 'wake',
528
- entityId: wake.wakeId,
529
- fromState: 'wake_queued',
530
- toState: 'dead_letter',
531
- details: {
532
- attemptCount: delivery?.attemptCount ?? claim.attemptCount,
533
- message:
534
- error instanceof Error
535
- ? error.message
536
- : 'wake delivery failed',
537
- },
538
- });
539
- return {
540
- status: 'dead_letter',
541
- wake: deadLetter,
542
- };
543
- }
544
- }
545
-
546
- async retryWake(wakeId) {
547
- const delivery = this.store.getWakeDelivery(wakeId);
548
- if (!delivery || delivery.sourceType !== 'event') {
549
- return { status: 'not_event_wake', wake: null };
550
- }
551
-
552
- const latest = this.store.latestWake(wakeId);
553
- if (!latest) {
554
- throw new Error(`Unknown wake: ${wakeId}`);
555
- }
556
- if (latest.status === 'handled') {
557
- return { status: 'handled', wake: latest };
558
- }
559
- if (latest.status === 'dead_letter') {
560
- return { status: 'dead_letter', wake: latest };
561
- }
562
-
563
- const trace = this.store.trace(delivery.sourceId);
564
- const eventRecord = trace.events[0];
565
- if (!eventRecord?.lineage || !eventRecord?.event) {
566
- const error = new Error(
567
- `Cannot reconstruct event wake ${wakeId}; trace ${delivery.sourceId} is missing`,
568
- );
569
- error.code = 'WAKE_RETRY_SOURCE_MISSING';
570
- throw error;
571
- }
572
-
573
- const decision = latest.decisionId
574
- ? trace.decisions.find(
575
- (candidate) => candidate.decisionId === latest.decisionId,
576
- ) ?? null
577
- : null;
578
-
579
- const queued = WakeRecordSchema.parse({
580
- ...latest,
581
- status: 'queued',
582
- runtimeReceiptId: undefined,
583
- });
584
- return this.deliverQueuedWake({
585
- wake: queued,
586
- lineage: eventRecord.lineage,
587
- event: eventRecord.event,
588
- decision,
589
- });
590
- }
591
-
592
- async acknowledgeWake(wakeId, runtimeReceiptId) {
593
- const latest = this.store.latestWake(wakeId);
594
- if (!latest) throw new Error('Unknown wake');
595
- if (latest.status === 'handled') return latest;
596
- if (latest.status !== 'delivered') {
597
- throw new Error(`Wake must be delivered before acknowledgement; current=${latest.status}`);
598
- }
599
-
600
- const handled = WakeRecordSchema.parse({
601
- ...latest,
602
- createdAt: nowIso(this.now),
603
- status: 'handled',
604
- runtimeReceiptId: runtimeReceiptId ?? latest.runtimeReceiptId,
605
- });
606
-
607
- await this.store.appendWake(handled);
608
- await this.store.appendAudit({
609
- auditId: await stableId('audit', `${latest.traceId}:wake-handled`),
610
- traceId: latest.traceId,
611
- timestamp: nowIso(this.now),
612
- kind: 'wake.handled',
613
- entityType: 'wake',
614
- entityId: wakeId,
615
- fromState: 'wake_delivered',
616
- toState: 'handled',
617
- details: {
618
- runtimeReceiptId: handled.runtimeReceiptId ?? null,
619
- },
620
- });
621
-
622
- return handled;
623
- }
624
- }
@@ -1,31 +0,0 @@
1
- export function createHttpWakeDeliverer(url, fetchFn = fetch) {
2
- if (!url) return null;
3
-
4
- return async (envelope) => {
5
- const response = await fetchFn(url, {
6
- method: 'POST',
7
- headers: {
8
- 'Content-Type': 'application/json',
9
- },
10
- body: JSON.stringify(envelope),
11
- });
12
-
13
- if (!response.ok) {
14
- throw new Error(`Runtime wake callback failed with HTTP ${response.status}`);
15
- }
16
-
17
- let body = {};
18
- try {
19
- body = await response.json();
20
- } catch {
21
- // A 2xx response without JSON is still a valid acceptance.
22
- }
23
-
24
- return {
25
- runtimeReceiptId:
26
- typeof body.runtimeReceiptId === 'string'
27
- ? body.runtimeReceiptId
28
- : undefined,
29
- };
30
- };
31
- }