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.
- package/CHANGELOG.md +41 -0
- package/README.md +31 -38
- package/SECURITY.md +4 -4
- package/bin/mcp-event-intelligence.mjs +2 -2
- package/dist/src/composite/engine.d.ts +11 -7
- package/dist/src/composite/engine.js +150 -414
- package/dist/src/composite/matching.d.ts +1 -5
- package/dist/src/composite/matching.js +0 -54
- package/dist/src/composite/runtimeSupport.js +10 -6
- package/dist/src/composite/store.d.ts +38 -4
- package/dist/src/composite/temporal.d.ts +0 -17
- package/dist/src/composite/temporal.js +0 -266
- package/dist/src/intelligenceProtocol/activationSchemas.d.ts +36 -3
- package/dist/src/intelligenceProtocol/activationSchemas.js +4 -3
- package/dist/src/intelligenceProtocol/index.d.ts +8 -12
- package/dist/src/intelligenceProtocol/index.js +4 -7
- package/dist/src/intelligenceProtocol/lifecycle.js +2 -10
- package/dist/src/intelligenceProtocol/operatorRegistry.d.ts +3 -270
- package/dist/src/intelligenceProtocol/operatorRegistry.js +5 -144
- package/dist/src/intelligenceProtocol/schemas.d.ts +0 -189
- package/dist/src/intelligenceProtocol/schemas.js +1 -97
- package/dist/src/intelligenceProtocol/triggerPlanSchemas.d.ts +5 -102
- package/dist/src/intelligenceProtocol/triggerPlanSchemas.js +2 -26
- package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +14 -215
- package/dist/src/intelligenceProtocol/triggerSchemas.js +18 -191
- package/dist/src/intelligenceProtocol/version.d.ts +2 -2
- package/dist/src/intelligenceProtocol/version.js +2 -2
- package/dist/src/patternV2/evaluator.d.ts +9 -1
- package/dist/src/patternV2/evaluator.js +63 -4
- package/dist/src/semantic/types.d.ts +6 -0
- package/dist/src/semantic/typesafeJevEvaluator.d.ts +1 -0
- package/dist/src/semantic/typesafeJevEvaluator.js +6 -0
- package/package.json +5 -2
- package/scripts/host-integration.d.mts +48 -5
- package/scripts/host-integration.mjs +16 -4
- package/scripts/lib/activation-hydrator.mjs +12 -12
- package/scripts/lib/composite-event-consumer.mjs +8 -28
- package/scripts/lib/composite-wake-coordinator.mjs +19 -19
- package/scripts/lib/derived-contract-registry.mjs +4 -4
- package/scripts/lib/derived-event-coordinator.mjs +11 -22
- package/scripts/lib/local-event-intelligence-runtime.mjs +35 -3
- package/scripts/lib/mcp-events-client.mjs +36 -32
- package/scripts/lib/persistent-event-store.mjs +228 -61
- package/scripts/lib/store-capabilities.mjs +88 -0
- package/scripts/lib/store-conformance.mjs +127 -0
- package/scripts/lib/temporal-deadline-scheduler.mjs +3 -3
- package/scripts/lib/trigger-control-plane.mjs +52 -57
- package/scripts/lib/trigger-inspector.mjs +48 -160
- package/scripts/lib/trigger-planner.mjs +37 -126
- package/scripts/lib/wake-retry-scheduler.mjs +2 -19
- package/scripts/mcp-stdio-server.mjs +8 -8
- package/scripts/service.mjs +19 -138
- package/server.json +3 -3
- package/dist/src/intelligenceProtocol/invariants.d.ts +0 -3
- package/dist/src/intelligenceProtocol/invariants.js +0 -36
- package/dist/src/intelligenceProtocol/mapper.d.ts +0 -14
- package/dist/src/intelligenceProtocol/mapper.js +0 -28
- package/dist/src/patternV2/legacyCompiler.d.ts +0 -3
- package/dist/src/patternV2/legacyCompiler.js +0 -184
- package/scripts/lib/event-processor.mjs +0 -624
- package/scripts/lib/http-wake-deliverer.mjs +0 -31
|
@@ -103,50 +103,6 @@ function validatePredicateAgainstSource(source, predicate, warnings) {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
function clauseSourceForRef(ref, clauses, resolvedSources) {
|
|
107
|
-
const index = clauses.findIndex((clause) => clause.id === ref);
|
|
108
|
-
if (index < 0) {
|
|
109
|
-
const error = new Error(`Temporal condition references unknown event id: ${ref}`);
|
|
110
|
-
error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
|
|
111
|
-
throw error;
|
|
112
|
-
}
|
|
113
|
-
return resolvedSources[index];
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function validateTemporalConditions(conditions, clauses, resolvedSources, warnings) {
|
|
117
|
-
const refs = new Set(clauses.map((clause) => clause.id));
|
|
118
|
-
|
|
119
|
-
for (const condition of conditions) {
|
|
120
|
-
const referenced = [];
|
|
121
|
-
if ('ref' in condition) referenced.push(condition.ref);
|
|
122
|
-
if ('afterRef' in condition) referenced.push(condition.afterRef);
|
|
123
|
-
if ('beforeRef' in condition) referenced.push(condition.beforeRef);
|
|
124
|
-
|
|
125
|
-
for (const ref of referenced) {
|
|
126
|
-
if (!refs.has(ref)) {
|
|
127
|
-
const error = new Error(
|
|
128
|
-
`Temporal condition ${condition.id} references unknown event id: ${ref}`,
|
|
129
|
-
);
|
|
130
|
-
error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
|
|
131
|
-
throw error;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (condition.kind === 'distinct') {
|
|
136
|
-
const source = clauseSourceForRef(
|
|
137
|
-
condition.ref,
|
|
138
|
-
clauses,
|
|
139
|
-
resolvedSources,
|
|
140
|
-
);
|
|
141
|
-
validatePredicateAgainstSource(
|
|
142
|
-
source,
|
|
143
|
-
{ path: condition.path, op: 'exists', value: true },
|
|
144
|
-
warnings,
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
106
|
function walkPatternValue(value, visitor) {
|
|
151
107
|
if (!value || typeof value !== 'object') return;
|
|
152
108
|
if (value.kind === 'field') {
|
|
@@ -189,32 +145,6 @@ function walkPatternNode(node, visitor) {
|
|
|
189
145
|
if (node.forbidden) walkPatternNode(node.forbidden, visitor);
|
|
190
146
|
}
|
|
191
147
|
|
|
192
|
-
function expressionFor(match, refs) {
|
|
193
|
-
if (typeof match === 'object' && match?.kind === 'count') {
|
|
194
|
-
if (!refs.includes(match.eventId)) {
|
|
195
|
-
const error = new Error(`Count match references unknown event id: ${match.eventId}`);
|
|
196
|
-
error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
|
|
197
|
-
throw error;
|
|
198
|
-
}
|
|
199
|
-
return { kind: 'count', ref: match.eventId, atLeast: match.atLeast };
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (match === 'sequence') {
|
|
203
|
-
if (refs.length < 2) {
|
|
204
|
-
const error = new Error('Sequence match requires at least two events');
|
|
205
|
-
error.code = 'TRIGGER_PLAN_SEQUENCE_REQUIRES_MULTIPLE_EVENTS';
|
|
206
|
-
throw error;
|
|
207
|
-
}
|
|
208
|
-
return { kind: 'sequence', refs };
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (match === 'any' || refs.length === 1) {
|
|
212
|
-
return { kind: 'anyOf', refs };
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return { kind: 'allOf', refs };
|
|
216
|
-
}
|
|
217
|
-
|
|
218
148
|
function lifecycleFor(input = {}) {
|
|
219
149
|
return {
|
|
220
150
|
oneShot: input.oneShot ?? false,
|
|
@@ -226,6 +156,31 @@ function lifecycleFor(input = {}) {
|
|
|
226
156
|
};
|
|
227
157
|
}
|
|
228
158
|
|
|
159
|
+
function defaultPattern(refs) {
|
|
160
|
+
const root = refs.length === 1
|
|
161
|
+
? { kind: 'event', ref: refs[0] }
|
|
162
|
+
: {
|
|
163
|
+
kind: 'allOf',
|
|
164
|
+
children: refs.map((ref) => ({ kind: 'event', ref })),
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
version: '2',
|
|
169
|
+
root,
|
|
170
|
+
partitionBy: [],
|
|
171
|
+
selection: {
|
|
172
|
+
overlap: 'disallow',
|
|
173
|
+
afterMatch: 'skipPastLast',
|
|
174
|
+
maxMatchesPerEvent: 10,
|
|
175
|
+
},
|
|
176
|
+
execution: {
|
|
177
|
+
maxCandidates: 512,
|
|
178
|
+
maxSemanticEvaluations: 16,
|
|
179
|
+
maxBufferedEvents: 10000,
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
229
184
|
export class TriggerPlanner {
|
|
230
185
|
constructor({ store }) {
|
|
231
186
|
this.store = store;
|
|
@@ -233,7 +188,7 @@ export class TriggerPlanner {
|
|
|
233
188
|
|
|
234
189
|
async plan(input) {
|
|
235
190
|
const plan = parseTriggerPlanInput(input);
|
|
236
|
-
const activeSources = this.store.listEventSources({ enabledOnly: true });
|
|
191
|
+
const activeSources = await this.store.listEventSources({ enabledOnly: true });
|
|
237
192
|
const warnings = [];
|
|
238
193
|
const resolvedSources = [];
|
|
239
194
|
const seenIds = new Set();
|
|
@@ -285,12 +240,13 @@ export class TriggerPlanner {
|
|
|
285
240
|
|
|
286
241
|
const refs = clauses.map((clause) => clause.id);
|
|
287
242
|
|
|
288
|
-
|
|
289
|
-
|
|
243
|
+
const pattern = plan.pattern ?? defaultPattern(refs);
|
|
244
|
+
{
|
|
245
|
+
const patternRefs = collectPatternRefs(pattern.root);
|
|
290
246
|
for (const ref of patternRefs) {
|
|
291
247
|
if (!refs.includes(ref)) {
|
|
292
248
|
const error = new Error(
|
|
293
|
-
`Pattern AST
|
|
249
|
+
`Pattern AST references unknown event id: ${ref}`,
|
|
294
250
|
);
|
|
295
251
|
error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
|
|
296
252
|
throw error;
|
|
@@ -301,7 +257,7 @@ export class TriggerPlanner {
|
|
|
301
257
|
const index = clauses.findIndex((candidate) => candidate.id === ref);
|
|
302
258
|
if (index < 0) {
|
|
303
259
|
const error = new Error(
|
|
304
|
-
`Pattern AST
|
|
260
|
+
`Pattern AST references unknown event id: ${ref}`,
|
|
305
261
|
);
|
|
306
262
|
error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
|
|
307
263
|
throw error;
|
|
@@ -309,7 +265,7 @@ export class TriggerPlanner {
|
|
|
309
265
|
return resolvedSources[index];
|
|
310
266
|
};
|
|
311
267
|
|
|
312
|
-
walkPatternNode(
|
|
268
|
+
walkPatternNode(pattern.root, (ref, fieldPath, purpose) => {
|
|
313
269
|
validatePredicateAgainstSource(
|
|
314
270
|
sourceForRef(ref),
|
|
315
271
|
{ path: fieldPath, op: 'exists', value: true },
|
|
@@ -317,7 +273,7 @@ export class TriggerPlanner {
|
|
|
317
273
|
);
|
|
318
274
|
});
|
|
319
275
|
|
|
320
|
-
for (const dimension of
|
|
276
|
+
for (const dimension of pattern.partitionBy) {
|
|
321
277
|
for (const field of dimension.fields) {
|
|
322
278
|
const clauseIndex = clauses.findIndex(
|
|
323
279
|
(candidate) => candidate.id === field.ref,
|
|
@@ -336,52 +292,14 @@ export class TriggerPlanner {
|
|
|
336
292
|
);
|
|
337
293
|
}
|
|
338
294
|
}
|
|
339
|
-
} else {
|
|
340
|
-
validateTemporalConditions(
|
|
341
|
-
plan.temporal,
|
|
342
|
-
clauses,
|
|
343
|
-
resolvedSources,
|
|
344
|
-
warnings,
|
|
345
|
-
);
|
|
346
295
|
}
|
|
347
296
|
|
|
348
|
-
const correlation = {};
|
|
349
|
-
if (!plan.patternV2 && plan.correlateBy) {
|
|
350
|
-
for (const field of plan.correlateBy) {
|
|
351
|
-
if (!refs.includes(field.eventId)) {
|
|
352
|
-
const error = new Error(
|
|
353
|
-
`Correlation references unknown event id: ${field.eventId}`,
|
|
354
|
-
);
|
|
355
|
-
error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
|
|
356
|
-
throw error;
|
|
357
|
-
}
|
|
358
|
-
const clause = clauses.find((candidate) => candidate.id === field.eventId);
|
|
359
|
-
const source = resolvedSources[clauses.indexOf(clause)];
|
|
360
|
-
validatePredicateAgainstSource(
|
|
361
|
-
source,
|
|
362
|
-
{ path: field.path, op: 'exists', value: true },
|
|
363
|
-
warnings,
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
correlation.deterministic = {
|
|
367
|
-
kind: 'same_value',
|
|
368
|
-
fields: plan.correlateBy.map((field) => ({
|
|
369
|
-
ref: field.eventId,
|
|
370
|
-
path: field.path,
|
|
371
|
-
})),
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
if (!plan.patternV2 && plan.semanticCorrelation) {
|
|
375
|
-
correlation.semantic = plan.semanticCorrelation;
|
|
376
|
-
}
|
|
377
297
|
|
|
378
298
|
const triggerId = plan.triggerId || `planned_${(
|
|
379
299
|
await sha256Hex(canonicalJson({
|
|
380
300
|
target: plan.target ?? null,
|
|
381
301
|
events: plan.events,
|
|
382
|
-
|
|
383
|
-
temporal: plan.temporal,
|
|
384
|
-
patternV2: plan.patternV2 ?? null,
|
|
302
|
+
pattern,
|
|
385
303
|
eventTime: plan.eventTime ?? null,
|
|
386
304
|
conditionOnly: plan.conditionOnly,
|
|
387
305
|
continuation: plan.continuation?.instruction ?? null,
|
|
@@ -401,27 +319,21 @@ export class TriggerPlanner {
|
|
|
401
319
|
conditionOnly: plan.conditionOnly,
|
|
402
320
|
...(plan.continuation ? { continuation: plan.continuation } : {}),
|
|
403
321
|
clauses,
|
|
404
|
-
|
|
405
|
-
? { kind: 'anyOf', refs }
|
|
406
|
-
: expressionFor(plan.match, refs),
|
|
407
|
-
temporal: plan.patternV2 ? [] : plan.temporal,
|
|
408
|
-
...(plan.patternV2 ? { patternV2: plan.patternV2 } : {}),
|
|
322
|
+
pattern,
|
|
409
323
|
withinMs: plan.withinMs,
|
|
410
324
|
...(plan.eventTime ? { eventTime: plan.eventTime } : {}),
|
|
411
325
|
lifecycle: lifecycleFor(plan.lifecycle),
|
|
412
|
-
...(Object.keys(correlation).length ? { correlation } : {}),
|
|
413
326
|
...(plan.target ? { target: plan.target } : {}),
|
|
414
327
|
});
|
|
415
328
|
|
|
416
329
|
return {
|
|
417
|
-
planVersion: '
|
|
330
|
+
planVersion: '2',
|
|
418
331
|
definition,
|
|
419
332
|
connectionIds: [...new Set(resolvedSources.map((source) => source.connectionId))],
|
|
420
333
|
resolvedSources: resolvedSources.map(sourceSummary),
|
|
421
334
|
warnings,
|
|
422
335
|
explanation: {
|
|
423
336
|
when: {
|
|
424
|
-
expression: definition.expression,
|
|
425
337
|
events: clauses.map((clause) => ({
|
|
426
338
|
id: clause.id,
|
|
427
339
|
event: clause.event,
|
|
@@ -429,8 +341,7 @@ export class TriggerPlanner {
|
|
|
429
341
|
arguments: clause.arguments,
|
|
430
342
|
where: clause.where,
|
|
431
343
|
})),
|
|
432
|
-
|
|
433
|
-
patternV2: definition.patternV2 ?? null,
|
|
344
|
+
pattern: definition.pattern,
|
|
434
345
|
withinMs: definition.withinMs,
|
|
435
346
|
eventTime: definition.eventTime ?? { allowedLatenessMs: 0 },
|
|
436
347
|
},
|
|
@@ -2,13 +2,11 @@ export class WakeRetryScheduler {
|
|
|
2
2
|
constructor({
|
|
3
3
|
store,
|
|
4
4
|
resolveCoordinator,
|
|
5
|
-
eventProcessor = null,
|
|
6
5
|
intervalMs = 1000,
|
|
7
6
|
now = () => new Date(),
|
|
8
7
|
}) {
|
|
9
8
|
this.store = store;
|
|
10
9
|
this.resolveCoordinator = resolveCoordinator;
|
|
11
|
-
this.eventProcessor = eventProcessor;
|
|
12
10
|
this.intervalMs = Math.max(100, Number(intervalMs) || 1000);
|
|
13
11
|
this.now = now;
|
|
14
12
|
this.timer = null;
|
|
@@ -19,25 +17,10 @@ export class WakeRetryScheduler {
|
|
|
19
17
|
if (this.running) return [];
|
|
20
18
|
this.running = true;
|
|
21
19
|
try {
|
|
22
|
-
const due = this.store.listDueWakeDeliveries(this.now().toISOString());
|
|
20
|
+
const due = await this.store.listDueWakeDeliveries(this.now().toISOString());
|
|
23
21
|
const outcomes = [];
|
|
24
22
|
|
|
25
23
|
for (const delivery of due) {
|
|
26
|
-
if (delivery.sourceType === 'event') {
|
|
27
|
-
if (!this.eventProcessor || typeof this.eventProcessor.retryWake !== 'function') {
|
|
28
|
-
outcomes.push({
|
|
29
|
-
wakeId: delivery.wakeId,
|
|
30
|
-
status: 'event_processor_unconfigured',
|
|
31
|
-
});
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
const result = await this.eventProcessor.retryWake(delivery.wakeId);
|
|
35
|
-
outcomes.push({
|
|
36
|
-
wakeId: delivery.wakeId,
|
|
37
|
-
status: result.status,
|
|
38
|
-
});
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
24
|
|
|
42
25
|
const coordinator = this.resolveCoordinator(delivery.runtime);
|
|
43
26
|
if (!coordinator) {
|
|
@@ -48,7 +31,7 @@ export class WakeRetryScheduler {
|
|
|
48
31
|
continue;
|
|
49
32
|
}
|
|
50
33
|
|
|
51
|
-
const match = this.store.listTriggerMatches(delivery.triggerId)
|
|
34
|
+
const match = (await this.store.listTriggerMatches(delivery.triggerId))
|
|
52
35
|
.filter((candidate) => candidate.matchId === delivery.matchId)
|
|
53
36
|
.sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt))[0];
|
|
54
37
|
|
|
@@ -93,7 +93,7 @@ function registerReadTools(server, runtime) {
|
|
|
93
93
|
async ({ connectionIds }) => {
|
|
94
94
|
try {
|
|
95
95
|
return jsonResult({
|
|
96
|
-
sources: runtime.triggerControl.listEventSources({
|
|
96
|
+
sources: await runtime.triggerControl.listEventSources({
|
|
97
97
|
...(connectionIds ? { connectionIds } : {}),
|
|
98
98
|
}),
|
|
99
99
|
});
|
|
@@ -147,7 +147,7 @@ function registerReadTools(server, runtime) {
|
|
|
147
147
|
async ({ ownerOnly }) => {
|
|
148
148
|
try {
|
|
149
149
|
return jsonResult({
|
|
150
|
-
triggers: runtime.triggerControl.listTriggers(
|
|
150
|
+
triggers: await runtime.triggerControl.listTriggers(
|
|
151
151
|
ownerOnly ? { owner: ownerFromEnv(process.env) } : {},
|
|
152
152
|
),
|
|
153
153
|
});
|
|
@@ -210,7 +210,7 @@ function registerReadTools(server, runtime) {
|
|
|
210
210
|
async ({ eventName }) => {
|
|
211
211
|
try {
|
|
212
212
|
return jsonResult({
|
|
213
|
-
contracts: runtime.store.listDerivedContracts(eventName),
|
|
213
|
+
contracts: await runtime.store.listDerivedContracts(eventName),
|
|
214
214
|
});
|
|
215
215
|
} catch (error) {
|
|
216
216
|
return errorResult(error);
|
|
@@ -229,7 +229,7 @@ function registerReadTools(server, runtime) {
|
|
|
229
229
|
},
|
|
230
230
|
async ({ wakeId }) => {
|
|
231
231
|
try {
|
|
232
|
-
return jsonResult(runtime.activationHydrator.hydrateWake(wakeId));
|
|
232
|
+
return jsonResult(await runtime.activationHydrator.hydrateWake(wakeId));
|
|
233
233
|
} catch (error) {
|
|
234
234
|
return errorResult(error);
|
|
235
235
|
}
|
|
@@ -247,11 +247,11 @@ function registerReadTools(server, runtime) {
|
|
|
247
247
|
try {
|
|
248
248
|
return jsonResult({
|
|
249
249
|
restored: runtime.restored,
|
|
250
|
-
hostMcpEventConnections: runtime.mcpEventsClient.status(),
|
|
250
|
+
hostMcpEventConnections: await runtime.mcpEventsClient.status(),
|
|
251
251
|
pendingTemporalDeadlines:
|
|
252
|
-
runtime.store.listTemporalDeadlines({ status: 'pending' }).length,
|
|
253
|
-
derivedEvents: runtime.store.listDerivedEvents().length,
|
|
254
|
-
derivedContracts: runtime.store.listDerivedContracts().length,
|
|
252
|
+
(await runtime.store.listTemporalDeadlines({ status: 'pending' })).length,
|
|
253
|
+
derivedEvents: (await runtime.store.listDerivedEvents()).length,
|
|
254
|
+
derivedContracts: (await runtime.store.listDerivedContracts()).length,
|
|
255
255
|
writeEnabled: process.env.MCP_WRITE_ENABLED === 'true',
|
|
256
256
|
});
|
|
257
257
|
} catch (error) {
|
package/scripts/service.mjs
CHANGED
|
@@ -13,13 +13,10 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
ExperimentalMcpEventsServer,
|
|
15
15
|
} from '../dist/src/mcpEvents/server.js';
|
|
16
|
-
import { EventProcessor } from './lib/event-processor.mjs';
|
|
17
16
|
import {
|
|
18
|
-
githubEventPassesStructuredFilter,
|
|
19
17
|
translateGitHubWebhook,
|
|
20
18
|
verifyGitHubWebhookSignature,
|
|
21
19
|
} from './lib/github-webhook.mjs';
|
|
22
|
-
import { createHttpWakeDeliverer } from './lib/http-wake-deliverer.mjs';
|
|
23
20
|
import {
|
|
24
21
|
CompositeWakeCoordinator,
|
|
25
22
|
} from './lib/composite-wake-coordinator.mjs';
|
|
@@ -65,10 +62,6 @@ const evaluator = process.env.TYPESAFE_API_KEY
|
|
|
65
62
|
? TypeSafeJevEvaluator.fromEnvironment(process.env)
|
|
66
63
|
: null;
|
|
67
64
|
|
|
68
|
-
const wakeDeliverer = createHttpWakeDeliverer(
|
|
69
|
-
process.env.RUNTIME_WAKE_URL ?? '',
|
|
70
|
-
);
|
|
71
|
-
|
|
72
65
|
const deliveryOptions = {
|
|
73
66
|
leaseMs: Number(process.env.WAKE_DELIVERY_LEASE_MS ?? 30000),
|
|
74
67
|
maxAttempts: Number(process.env.WAKE_DELIVERY_MAX_ATTEMPTS ?? 5),
|
|
@@ -76,12 +69,6 @@ const deliveryOptions = {
|
|
|
76
69
|
retryMaxDelayMs: Number(process.env.WAKE_RETRY_MAX_DELAY_MS ?? 60000),
|
|
77
70
|
};
|
|
78
71
|
|
|
79
|
-
const processor = new EventProcessor({
|
|
80
|
-
store,
|
|
81
|
-
evaluator,
|
|
82
|
-
wakeDeliverer,
|
|
83
|
-
...deliveryOptions,
|
|
84
|
-
});
|
|
85
72
|
const compositeTriggers = new CompositeTriggerEngine(store, evaluator);
|
|
86
73
|
const runtimeWakeTargets = readRuntimeWakeTargets(
|
|
87
74
|
process.env.RUNTIME_WAKE_TARGETS_JSON,
|
|
@@ -104,7 +91,6 @@ const compositeEventConsumer = new CompositeEventConsumer({
|
|
|
104
91
|
triggerEngine: compositeTriggers,
|
|
105
92
|
wakeCoordinators,
|
|
106
93
|
derivedEventCoordinator,
|
|
107
|
-
processor,
|
|
108
94
|
maxDerivedDepth: Number(process.env.MAX_DERIVED_EVENT_DEPTH ?? 16),
|
|
109
95
|
});
|
|
110
96
|
const temporalScheduler = new TemporalDeadlineScheduler({
|
|
@@ -116,7 +102,6 @@ temporalScheduler.start();
|
|
|
116
102
|
|
|
117
103
|
const wakeRetryScheduler = new WakeRetryScheduler({
|
|
118
104
|
store,
|
|
119
|
-
eventProcessor: processor,
|
|
120
105
|
resolveCoordinator: (runtime) => wakeCoordinators.get(runtime) ?? null,
|
|
121
106
|
intervalMs: Number(process.env.WAKE_RETRY_TICK_MS ?? 1000),
|
|
122
107
|
});
|
|
@@ -167,59 +152,6 @@ function requireBearer(request, response) {
|
|
|
167
152
|
return true;
|
|
168
153
|
}
|
|
169
154
|
|
|
170
|
-
function githubSemanticCondition() {
|
|
171
|
-
const instruction = process.env.SEMANTIC_INSTRUCTION;
|
|
172
|
-
if (!instruction) return undefined;
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
type: 'semantic_boolean',
|
|
176
|
-
instruction,
|
|
177
|
-
input: ['data.title', 'data.bodyPreview', 'data.labels'],
|
|
178
|
-
matchThreshold: Number(process.env.SEMANTIC_MATCH_THRESHOLD ?? 0.8),
|
|
179
|
-
rejectThreshold: Number(process.env.SEMANTIC_REJECT_THRESHOLD ?? 0.2),
|
|
180
|
-
uncertain: process.env.SEMANTIC_UNCERTAIN_POLICY ?? 'escalate',
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function logProcessingResult(result) {
|
|
185
|
-
console.log(
|
|
186
|
-
JSON.stringify({
|
|
187
|
-
message: 'event_intelligence_result',
|
|
188
|
-
traceId: result.traceId,
|
|
189
|
-
sourceEventId: result.sourceEventId,
|
|
190
|
-
status: result.status,
|
|
191
|
-
decisionId: result.decision?.decisionId ?? null,
|
|
192
|
-
outcome: result.decision?.outcome ?? null,
|
|
193
|
-
probability: result.decision?.probability ?? null,
|
|
194
|
-
evaluator: result.decision?.evaluator ?? null,
|
|
195
|
-
providerEvidence: result.decision?.providerEvidence ?? null,
|
|
196
|
-
wakeId: result.wake?.wakeId ?? null,
|
|
197
|
-
wakeStatus: result.wake?.status ?? null,
|
|
198
|
-
}),
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function githubContext() {
|
|
203
|
-
const targetId = process.env.RUNTIME_TARGET_ID;
|
|
204
|
-
if (!targetId) {
|
|
205
|
-
throw new Error('RUNTIME_TARGET_ID is required for GitHub webhook delivery');
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
environmentId,
|
|
210
|
-
subscriptionId:
|
|
211
|
-
process.env.GITHUB_SUBSCRIPTION_ID ?? 'github-issues-default',
|
|
212
|
-
serverId: process.env.GITHUB_SERVER_ID ?? 'github-webhook-adapter',
|
|
213
|
-
transport: 'webhook',
|
|
214
|
-
provider: 'github',
|
|
215
|
-
target: {
|
|
216
|
-
runtime: process.env.RUNTIME_TARGET_RUNTIME ?? 'agent-runtime',
|
|
217
|
-
kind: process.env.RUNTIME_TARGET_KIND ?? 'session',
|
|
218
|
-
id: targetId,
|
|
219
|
-
},
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
|
|
223
155
|
const server = createServer(async (request, response) => {
|
|
224
156
|
try {
|
|
225
157
|
const url = new URL(
|
|
@@ -237,28 +169,20 @@ const server = createServer(async (request, response) => {
|
|
|
237
169
|
|
|
238
170
|
if (request.method === 'GET' && url.pathname === '/readyz') {
|
|
239
171
|
const auditValid = await store.verifyAudit();
|
|
240
|
-
const semanticRequired = Boolean(process.env.SEMANTIC_INSTRUCTION);
|
|
241
172
|
const evaluatorConfigured = Boolean(evaluator);
|
|
242
|
-
const ready =
|
|
243
|
-
auditValid &&
|
|
244
|
-
(!semanticRequired || evaluatorConfigured);
|
|
173
|
+
const ready = auditValid;
|
|
245
174
|
|
|
246
175
|
return sendJson(response, ready ? 200 : 503, {
|
|
247
|
-
status: ready
|
|
248
|
-
? 'ready'
|
|
249
|
-
: !auditValid
|
|
250
|
-
? 'audit_invalid'
|
|
251
|
-
: 'semantic_evaluator_unavailable',
|
|
176
|
+
status: ready ? 'ready' : 'audit_invalid',
|
|
252
177
|
auditValid,
|
|
253
|
-
semanticRequired,
|
|
254
178
|
evaluatorConfigured,
|
|
255
|
-
wakeCallbackConfigured:
|
|
179
|
+
wakeCallbackConfigured: runtimeWakeTargets.size > 0,
|
|
256
180
|
genericRuntimeWakeTargets: [...runtimeWakeTargets.keys()],
|
|
257
|
-
eventSources: store.listEventSources().length,
|
|
181
|
+
eventSources: (await store.listEventSources()).length,
|
|
258
182
|
pendingTemporalDeadlines:
|
|
259
|
-
store.listTemporalDeadlines({ status: 'pending' }).length,
|
|
260
|
-
derivedEvents: store.listDerivedEvents().length,
|
|
261
|
-
derivedContracts: store.listDerivedContracts().length,
|
|
183
|
+
(await store.listTemporalDeadlines({ status: 'pending' })).length,
|
|
184
|
+
derivedEvents: (await store.listDerivedEvents()).length,
|
|
185
|
+
derivedContracts: (await store.listDerivedContracts()).length,
|
|
262
186
|
});
|
|
263
187
|
}
|
|
264
188
|
|
|
@@ -338,27 +262,11 @@ const server = createServer(async (request, response) => {
|
|
|
338
262
|
}));
|
|
339
263
|
}
|
|
340
264
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
})
|
|
346
|
-
) {
|
|
347
|
-
return sendJson(response, 202, {
|
|
348
|
-
status: 'filtered_structured',
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const result = await processor.ingest({
|
|
353
|
-
event: translated.event,
|
|
354
|
-
context: githubContext(),
|
|
355
|
-
semanticCondition: githubSemanticCondition(),
|
|
356
|
-
wakeOnEscalation:
|
|
357
|
-
process.env.WAKE_ON_ESCALATION === 'true',
|
|
265
|
+
return sendJson(response, 202, {
|
|
266
|
+
status: mcpReceipt.accepted ? 'accepted' : 'duplicate',
|
|
267
|
+
mcpReceipt,
|
|
268
|
+
composite,
|
|
358
269
|
});
|
|
359
|
-
|
|
360
|
-
logProcessingResult(result);
|
|
361
|
-
return sendJson(response, 202, result);
|
|
362
270
|
}
|
|
363
271
|
|
|
364
272
|
if (request.method === 'POST' && url.pathname === '/mcp') {
|
|
@@ -422,7 +330,7 @@ const server = createServer(async (request, response) => {
|
|
|
422
330
|
url.pathname === '/v1/derived-contracts'
|
|
423
331
|
) {
|
|
424
332
|
return sendJson(response, 200, {
|
|
425
|
-
contracts: store.listDerivedContracts(
|
|
333
|
+
contracts: await store.listDerivedContracts(
|
|
426
334
|
url.searchParams.get('eventName') ?? undefined,
|
|
427
335
|
),
|
|
428
336
|
});
|
|
@@ -433,7 +341,7 @@ const server = createServer(async (request, response) => {
|
|
|
433
341
|
url.pathname === '/v1/derived-events'
|
|
434
342
|
) {
|
|
435
343
|
return sendJson(response, 200, {
|
|
436
|
-
events: store.listDerivedEvents({
|
|
344
|
+
events: await store.listDerivedEvents({
|
|
437
345
|
...(url.searchParams.get('triggerId')
|
|
438
346
|
? { triggerId: url.searchParams.get('triggerId') }
|
|
439
347
|
: {}),
|
|
@@ -454,7 +362,7 @@ const server = createServer(async (request, response) => {
|
|
|
454
362
|
const triggerId = decodeURIComponent(
|
|
455
363
|
url.pathname.slice('/v1/inspector/triggers/'.length),
|
|
456
364
|
);
|
|
457
|
-
return sendJson(response, 200, triggerInspector.inspect({
|
|
365
|
+
return sendJson(response, 200, await triggerInspector.inspect({
|
|
458
366
|
triggerId,
|
|
459
367
|
version: url.searchParams.get('version') ?? undefined,
|
|
460
368
|
matchId: url.searchParams.get('matchId') ?? undefined,
|
|
@@ -481,7 +389,7 @@ const server = createServer(async (request, response) => {
|
|
|
481
389
|
url.pathname === '/v1/temporal/deadlines'
|
|
482
390
|
) {
|
|
483
391
|
return sendJson(response, 200, {
|
|
484
|
-
deadlines: store.listTemporalDeadlines({
|
|
392
|
+
deadlines: await store.listTemporalDeadlines({
|
|
485
393
|
...(url.searchParams.get('triggerId')
|
|
486
394
|
? { triggerId: url.searchParams.get('triggerId') }
|
|
487
395
|
: {}),
|
|
@@ -507,7 +415,7 @@ const server = createServer(async (request, response) => {
|
|
|
507
415
|
if (request.method === 'GET' && url.pathname === '/v1/event-sources') {
|
|
508
416
|
const connectionIds = url.searchParams.getAll('connectionId');
|
|
509
417
|
return sendJson(response, 200, {
|
|
510
|
-
eventSources: triggerControl.listEventSources({
|
|
418
|
+
eventSources: await triggerControl.listEventSources({
|
|
511
419
|
...(connectionIds.length ? { connectionIds } : {}),
|
|
512
420
|
}),
|
|
513
421
|
});
|
|
@@ -528,7 +436,7 @@ const server = createServer(async (request, response) => {
|
|
|
528
436
|
const ownerId = url.searchParams.get('ownerId');
|
|
529
437
|
const tenantId = url.searchParams.get('tenantId');
|
|
530
438
|
return sendJson(response, 200, {
|
|
531
|
-
triggers: triggerControl.listTriggers({
|
|
439
|
+
triggers: await triggerControl.listTriggers({
|
|
532
440
|
...(ownerType && ownerId
|
|
533
441
|
? {
|
|
534
442
|
owner: {
|
|
@@ -633,14 +541,14 @@ const server = createServer(async (request, response) => {
|
|
|
633
541
|
|
|
634
542
|
if (request.method === 'GET' && url.pathname === '/v1/triggers') {
|
|
635
543
|
return sendJson(response, 200, {
|
|
636
|
-
triggers: store.listTriggers(),
|
|
544
|
+
triggers: await store.listTriggers(),
|
|
637
545
|
});
|
|
638
546
|
}
|
|
639
547
|
|
|
640
548
|
if (request.method === 'GET' && url.pathname === '/v1/trigger-matches') {
|
|
641
549
|
const triggerId = url.searchParams.get('triggerId') ?? undefined;
|
|
642
550
|
return sendJson(response, 200, {
|
|
643
|
-
matches: store.listTriggerMatches(triggerId),
|
|
551
|
+
matches: await store.listTriggerMatches(triggerId),
|
|
644
552
|
});
|
|
645
553
|
}
|
|
646
554
|
|
|
@@ -704,13 +612,6 @@ const server = createServer(async (request, response) => {
|
|
|
704
612
|
});
|
|
705
613
|
}
|
|
706
614
|
|
|
707
|
-
if (request.method === 'POST' && url.pathname === '/v1/events/ingest') {
|
|
708
|
-
const raw = await readRawBody(request);
|
|
709
|
-
const body = JSON.parse(raw.toString('utf8'));
|
|
710
|
-
const result = await processor.ingest(body);
|
|
711
|
-
logProcessingResult(result);
|
|
712
|
-
return sendJson(response, 202, result);
|
|
713
|
-
}
|
|
714
615
|
|
|
715
616
|
if (request.method === 'GET' && url.pathname === '/v1/audit') {
|
|
716
617
|
const afterSequence = Number(url.searchParams.get('afterSequence') ?? -1);
|
|
@@ -731,26 +632,6 @@ const server = createServer(async (request, response) => {
|
|
|
731
632
|
return sendJson(response, 200, store.trace(traceId));
|
|
732
633
|
}
|
|
733
634
|
|
|
734
|
-
if (
|
|
735
|
-
request.method === 'POST' &&
|
|
736
|
-
url.pathname.startsWith('/v1/wakes/') &&
|
|
737
|
-
url.pathname.endsWith('/ack')
|
|
738
|
-
) {
|
|
739
|
-
const wakeId = decodeURIComponent(
|
|
740
|
-
url.pathname.slice('/v1/wakes/'.length, -'/ack'.length),
|
|
741
|
-
);
|
|
742
|
-
const raw = await readRawBody(request);
|
|
743
|
-
const body = raw.length
|
|
744
|
-
? JSON.parse(raw.toString('utf8'))
|
|
745
|
-
: {};
|
|
746
|
-
const handled = await processor.acknowledgeWake(
|
|
747
|
-
wakeId,
|
|
748
|
-
typeof body.runtimeReceiptId === 'string'
|
|
749
|
-
? body.runtimeReceiptId
|
|
750
|
-
: undefined,
|
|
751
|
-
);
|
|
752
|
-
return sendJson(response, 200, handled);
|
|
753
|
-
}
|
|
754
635
|
|
|
755
636
|
return sendJson(response, 404, { error: 'NOT_FOUND' });
|
|
756
637
|
} catch (error) {
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.sarooo17/event-intelligence",
|
|
4
4
|
"title": "MCP Event Intelligence",
|
|
5
5
|
"description": "MCP-native CEP for durable agents: event-time patterns, semantic predicates, derived events.",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.6.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"url": "https://github.com/sarooo17/event-intelligence",
|
|
9
9
|
"source": "github"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "mcp-event-intelligence",
|
|
16
|
-
"version": "0.
|
|
16
|
+
"version": "0.6.0",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
19
19
|
},
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
"name": "TYPESAFE_API_KEY",
|
|
52
|
-
"description": "Optional TypeSafe Jev API key for semantic Pattern AST nodes
|
|
52
|
+
"description": "Optional TypeSafe Jev API key for explicit semantic Pattern AST nodes.",
|
|
53
53
|
"isRequired": false,
|
|
54
54
|
"isSecret": true,
|
|
55
55
|
"format": "string"
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import type { EventLineage, SemanticDecisionRecord, WakeRecord } from './schemas.js';
|
|
2
|
-
export declare function assertDecisionLineage(event: EventLineage, decision: SemanticDecisionRecord): void;
|
|
3
|
-
export declare function assertWakeLineage(event: EventLineage, decision: SemanticDecisionRecord | null, wake: WakeRecord): void;
|