mcp-event-intelligence 0.1.1 → 0.2.1
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 +72 -0
- package/README.md +79 -4
- package/bin/mcp-event-intelligence.mjs +1 -1
- package/dist/src/composite/derivedGraph.d.ts +4 -0
- package/dist/src/composite/derivedGraph.js +46 -0
- package/dist/src/composite/engine.d.ts +3 -64
- package/dist/src/composite/engine.js +26 -343
- package/dist/src/composite/matching.d.ts +8 -0
- package/dist/src/composite/matching.js +109 -0
- package/dist/src/composite/runtimeSupport.d.ts +24 -0
- package/dist/src/composite/runtimeSupport.js +176 -0
- package/dist/src/composite/store.d.ts +59 -0
- package/dist/src/composite/store.js +1 -0
- package/dist/src/intelligenceProtocol/schemas.d.ts +9 -7
- package/dist/src/intelligenceProtocol/schemas.js +1 -0
- package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +6 -6
- package/dist/src/mcpEvents/erpnextProvider.d.ts +20 -0
- package/dist/src/mcpEvents/erpnextProvider.js +80 -0
- package/package.json +5 -1
- package/scripts/host-integration.d.mts +34 -4
- package/scripts/host-integration.mjs +44 -1
- package/scripts/lib/composite-wake-coordinator.mjs +233 -9
- package/scripts/lib/event-processor.mjs +235 -20
- package/scripts/lib/local-event-intelligence-runtime.mjs +238 -83
- package/scripts/lib/mcp-events-client.mjs +184 -66
- package/scripts/lib/persistent-event-store.mjs +320 -1
- package/scripts/lib/temporal-deadline-scheduler.mjs +12 -2
- package/scripts/lib/wake-retry-scheduler.mjs +104 -0
- package/scripts/mcp-stdio-server.mjs +1 -1
- package/scripts/service.mjs +20 -0
- package/server.json +2 -2
|
@@ -22,7 +22,9 @@ import {
|
|
|
22
22
|
TriggerControlPlane,
|
|
23
23
|
} from './trigger-control-plane.mjs';
|
|
24
24
|
import {
|
|
25
|
+
DEFAULT_EVENT_SCOPE_ID,
|
|
25
26
|
PersistentEventStore,
|
|
27
|
+
normalizeEventScopeId,
|
|
26
28
|
} from './persistent-event-store.mjs';
|
|
27
29
|
import {
|
|
28
30
|
TriggerInspector,
|
|
@@ -33,6 +35,66 @@ import {
|
|
|
33
35
|
import {
|
|
34
36
|
McpEventsClientManager,
|
|
35
37
|
} from './mcp-events-client.mjs';
|
|
38
|
+
import {
|
|
39
|
+
WakeRetryScheduler,
|
|
40
|
+
} from './wake-retry-scheduler.mjs';
|
|
41
|
+
|
|
42
|
+
const REQUIRED_STORE_METHODS = [
|
|
43
|
+
'putTrigger',
|
|
44
|
+
'listTriggers',
|
|
45
|
+
'getTriggerState',
|
|
46
|
+
'setTriggerState',
|
|
47
|
+
'appendTriggerMatch',
|
|
48
|
+
'listTriggerMatches',
|
|
49
|
+
'putEventSource',
|
|
50
|
+
'listEventSources',
|
|
51
|
+
'appendWake',
|
|
52
|
+
'latestWake',
|
|
53
|
+
'ensureWakeDelivery',
|
|
54
|
+
'getWakeDelivery',
|
|
55
|
+
'claimWakeDelivery',
|
|
56
|
+
'completeWakeDelivery',
|
|
57
|
+
'failWakeDelivery',
|
|
58
|
+
'listDueWakeDeliveries',
|
|
59
|
+
'appendAudit',
|
|
60
|
+
'auditLength',
|
|
61
|
+
'getMcpClientState',
|
|
62
|
+
'putMcpClientState',
|
|
63
|
+
'appendMcpOccurrence',
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
function assertStoreContract(store) {
|
|
67
|
+
if (!store || typeof store !== 'object') {
|
|
68
|
+
throw new Error('Event Intelligence store must be an object');
|
|
69
|
+
}
|
|
70
|
+
const missing = REQUIRED_STORE_METHODS.filter(
|
|
71
|
+
(method) => typeof store[method] !== 'function',
|
|
72
|
+
);
|
|
73
|
+
if (missing.length) {
|
|
74
|
+
const error = new Error(
|
|
75
|
+
`Event Intelligence store is missing required methods: ${missing.join(', ')}`,
|
|
76
|
+
);
|
|
77
|
+
error.code = 'EVENT_INTELLIGENCE_STORE_INVALID';
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
return store;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function initializeStore(store) {
|
|
84
|
+
const restored =
|
|
85
|
+
typeof store.init === 'function'
|
|
86
|
+
? await store.init()
|
|
87
|
+
: {};
|
|
88
|
+
assertStoreContract(store);
|
|
89
|
+
return restored;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function scopedPacketBuilder(scopeId) {
|
|
93
|
+
return (input) => ({
|
|
94
|
+
...buildGenericRuntimeWakePacket(input),
|
|
95
|
+
scope_id: scopeId,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
36
98
|
|
|
37
99
|
export async function createLocalEventIntelligenceRuntime({
|
|
38
100
|
env = process.env,
|
|
@@ -40,10 +102,11 @@ export async function createLocalEventIntelligenceRuntime({
|
|
|
40
102
|
wakeHandlers = {},
|
|
41
103
|
wake,
|
|
42
104
|
semanticEvaluator,
|
|
105
|
+
store: providedStore,
|
|
43
106
|
} = {}) {
|
|
44
107
|
const dataDir = env.DATA_DIR ?? './data';
|
|
45
|
-
const
|
|
46
|
-
const restored = await
|
|
108
|
+
const rootStore = providedStore ?? new PersistentEventStore(dataDir);
|
|
109
|
+
const restored = await initializeStore(rootStore);
|
|
47
110
|
|
|
48
111
|
const evaluator =
|
|
49
112
|
semanticEvaluator !== undefined
|
|
@@ -52,42 +115,106 @@ export async function createLocalEventIntelligenceRuntime({
|
|
|
52
115
|
? TypeSafeJevEvaluator.fromEnvironment(env)
|
|
53
116
|
: null;
|
|
54
117
|
|
|
55
|
-
const triggerEngine = new CompositeTriggerEngine(store, evaluator);
|
|
56
|
-
|
|
57
|
-
const wakeCoordinators = new Map();
|
|
58
|
-
|
|
59
118
|
const runtimeWakeTargets = readRuntimeWakeTargets(
|
|
60
119
|
env.RUNTIME_WAKE_TARGETS_JSON,
|
|
61
120
|
);
|
|
62
|
-
for (const [runtime, target] of runtimeWakeTargets) {
|
|
63
|
-
wakeCoordinators.set(
|
|
64
|
-
runtime,
|
|
65
|
-
new CompositeWakeCoordinator({
|
|
66
|
-
store,
|
|
67
|
-
triggerEngine,
|
|
68
|
-
deliverer: createSignedRuntimeWakeDeliverer(target),
|
|
69
|
-
packetBuilder: buildGenericRuntimeWakePacket,
|
|
70
|
-
}),
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
121
|
const hostWakeEntries =
|
|
75
122
|
wakeHandlers instanceof Map
|
|
76
123
|
? [...wakeHandlers.entries()]
|
|
77
124
|
: Object.entries(wakeHandlers || {});
|
|
78
125
|
|
|
126
|
+
if (wake !== undefined && typeof wake !== 'function') {
|
|
127
|
+
throw new Error('Host wake dispatcher must be a function');
|
|
128
|
+
}
|
|
79
129
|
for (const [runtime, handler] of hostWakeEntries) {
|
|
80
130
|
if (typeof handler !== 'function') {
|
|
81
131
|
throw new Error(`Host wake handler for ${runtime} must be a function`);
|
|
82
132
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const scopeContexts = new Map();
|
|
136
|
+
|
|
137
|
+
const createScopeContext = async (scopeIdInput = DEFAULT_EVENT_SCOPE_ID) => {
|
|
138
|
+
const scopeId = normalizeEventScopeId(scopeIdInput);
|
|
139
|
+
const cached = scopeContexts.get(scopeId);
|
|
140
|
+
if (cached) return cached;
|
|
141
|
+
|
|
142
|
+
let store;
|
|
143
|
+
if (scopeId === DEFAULT_EVENT_SCOPE_ID) {
|
|
144
|
+
store = rootStore;
|
|
145
|
+
} else {
|
|
146
|
+
if (typeof rootStore.forScope !== 'function') {
|
|
147
|
+
const error = new Error(
|
|
148
|
+
`Configured Event Intelligence store does not support scope ${scopeId}`,
|
|
149
|
+
);
|
|
150
|
+
error.code = 'EVENT_INTELLIGENCE_STORE_SCOPE_UNSUPPORTED';
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
store = await rootStore.forScope(scopeId);
|
|
154
|
+
assertStoreContract(store);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const triggerEngine = new CompositeTriggerEngine(store, evaluator);
|
|
158
|
+
const wakeCoordinators = new Map();
|
|
159
|
+
const packetBuilder = scopedPacketBuilder(scopeId);
|
|
160
|
+
const deliveryOptions = {
|
|
161
|
+
leaseMs: Number(env.WAKE_DELIVERY_LEASE_MS ?? 30000),
|
|
162
|
+
maxAttempts: Number(env.WAKE_DELIVERY_MAX_ATTEMPTS ?? 5),
|
|
163
|
+
retryBaseDelayMs: Number(env.WAKE_RETRY_BASE_DELAY_MS ?? 1000),
|
|
164
|
+
retryMaxDelayMs: Number(env.WAKE_RETRY_MAX_DELAY_MS ?? 60000),
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
for (const [runtime, target] of runtimeWakeTargets) {
|
|
168
|
+
wakeCoordinators.set(
|
|
169
|
+
runtime,
|
|
170
|
+
new CompositeWakeCoordinator({
|
|
171
|
+
store,
|
|
172
|
+
triggerEngine,
|
|
173
|
+
deliverer: createSignedRuntimeWakeDeliverer(target),
|
|
174
|
+
packetBuilder,
|
|
175
|
+
...deliveryOptions,
|
|
176
|
+
}),
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
for (const [runtime, handler] of hostWakeEntries) {
|
|
181
|
+
wakeCoordinators.set(
|
|
182
|
+
runtime,
|
|
183
|
+
new CompositeWakeCoordinator({
|
|
184
|
+
store,
|
|
185
|
+
triggerEngine,
|
|
186
|
+
packetBuilder,
|
|
187
|
+
...deliveryOptions,
|
|
188
|
+
deliverer: async (packet) => {
|
|
189
|
+
const result = await handler(packet);
|
|
190
|
+
if (typeof result === 'string' && result) {
|
|
191
|
+
return { runtimeReceiptId: result };
|
|
192
|
+
}
|
|
193
|
+
if (
|
|
194
|
+
result &&
|
|
195
|
+
typeof result === 'object' &&
|
|
196
|
+
typeof result.runtimeReceiptId === 'string' &&
|
|
197
|
+
result.runtimeReceiptId
|
|
198
|
+
) {
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
throw new Error(
|
|
202
|
+
`Host wake handler for ${runtime} must return runtimeReceiptId`,
|
|
203
|
+
);
|
|
204
|
+
},
|
|
205
|
+
}),
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let wakeCoordinator = null;
|
|
210
|
+
if (wake) {
|
|
211
|
+
wakeCoordinator = new CompositeWakeCoordinator({
|
|
86
212
|
store,
|
|
87
213
|
triggerEngine,
|
|
88
|
-
packetBuilder
|
|
214
|
+
packetBuilder,
|
|
215
|
+
...deliveryOptions,
|
|
89
216
|
deliverer: async (packet) => {
|
|
90
|
-
const result = await
|
|
217
|
+
const result = await wake(packet);
|
|
91
218
|
if (typeof result === 'string' && result) {
|
|
92
219
|
return { runtimeReceiptId: result };
|
|
93
220
|
}
|
|
@@ -99,70 +226,84 @@ export async function createLocalEventIntelligenceRuntime({
|
|
|
99
226
|
) {
|
|
100
227
|
return result;
|
|
101
228
|
}
|
|
102
|
-
throw new Error(
|
|
103
|
-
`Host wake handler for ${runtime} must return runtimeReceiptId`,
|
|
104
|
-
);
|
|
229
|
+
throw new Error('Host wake dispatcher must return runtimeReceiptId');
|
|
105
230
|
},
|
|
106
|
-
})
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
let wakeCoordinator = null;
|
|
111
|
-
if (wake !== undefined) {
|
|
112
|
-
if (typeof wake !== 'function') {
|
|
113
|
-
throw new Error('Host wake dispatcher must be a function');
|
|
231
|
+
});
|
|
114
232
|
}
|
|
115
|
-
|
|
233
|
+
|
|
234
|
+
const derivedEventCoordinator = new DerivedEventCoordinator({ store });
|
|
235
|
+
const compositeEventConsumer = new CompositeEventConsumer({
|
|
116
236
|
store,
|
|
117
237
|
triggerEngine,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return { runtimeReceiptId: result };
|
|
123
|
-
}
|
|
124
|
-
if (
|
|
125
|
-
result &&
|
|
126
|
-
typeof result === 'object' &&
|
|
127
|
-
typeof result.runtimeReceiptId === 'string' &&
|
|
128
|
-
result.runtimeReceiptId
|
|
129
|
-
) {
|
|
130
|
-
return result;
|
|
131
|
-
}
|
|
132
|
-
throw new Error('Host wake dispatcher must return runtimeReceiptId');
|
|
133
|
-
},
|
|
238
|
+
wakeCoordinator,
|
|
239
|
+
wakeCoordinators,
|
|
240
|
+
derivedEventCoordinator,
|
|
241
|
+
maxDerivedDepth: Number(env.MAX_DERIVED_EVENT_DEPTH ?? 16),
|
|
134
242
|
});
|
|
135
|
-
}
|
|
136
243
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
derivedEventCoordinator,
|
|
144
|
-
maxDerivedDepth: Number(env.MAX_DERIVED_EVENT_DEPTH ?? 16),
|
|
145
|
-
});
|
|
244
|
+
const temporalScheduler = new TemporalDeadlineScheduler({
|
|
245
|
+
store,
|
|
246
|
+
compositeEventConsumer,
|
|
247
|
+
intervalMs: Number(env.TEMPORAL_TICK_MS ?? 1000),
|
|
248
|
+
});
|
|
249
|
+
temporalScheduler.start();
|
|
146
250
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
temporalScheduler.start();
|
|
251
|
+
const triggerControl = new TriggerControlPlane({
|
|
252
|
+
store,
|
|
253
|
+
triggerEngine,
|
|
254
|
+
});
|
|
255
|
+
const triggerInspector = new TriggerInspector({ store });
|
|
153
256
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
257
|
+
const coordinatorFor = (runtime) =>
|
|
258
|
+
wakeCoordinators.get(runtime) ?? wakeCoordinator;
|
|
259
|
+
const wakeRetryScheduler = new WakeRetryScheduler({
|
|
260
|
+
store,
|
|
261
|
+
resolveCoordinator: coordinatorFor,
|
|
262
|
+
intervalMs: Number(env.WAKE_RETRY_TICK_MS ?? 1000),
|
|
263
|
+
});
|
|
264
|
+
wakeRetryScheduler.start();
|
|
265
|
+
|
|
266
|
+
const context = {
|
|
267
|
+
scopeId,
|
|
268
|
+
store,
|
|
269
|
+
evaluator,
|
|
270
|
+
triggerEngine,
|
|
271
|
+
triggerControl,
|
|
272
|
+
triggerInspector,
|
|
273
|
+
compositeEventConsumer,
|
|
274
|
+
temporalScheduler,
|
|
275
|
+
wakeRetryScheduler,
|
|
276
|
+
wakeCoordinator,
|
|
277
|
+
wakeCoordinators,
|
|
278
|
+
async close() {
|
|
279
|
+
await Promise.all([
|
|
280
|
+
temporalScheduler.close(),
|
|
281
|
+
wakeRetryScheduler.close(),
|
|
282
|
+
]);
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
scopeContexts.set(scopeId, context);
|
|
286
|
+
return context;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const defaultContext = await createScopeContext(DEFAULT_EVENT_SCOPE_ID);
|
|
290
|
+
|
|
291
|
+
if (typeof rootStore.listScopeIds === 'function') {
|
|
292
|
+
const restoredScopes = await rootStore.listScopeIds();
|
|
293
|
+
for (const scopeId of restoredScopes) {
|
|
294
|
+
if (scopeId !== DEFAULT_EVENT_SCOPE_ID) {
|
|
295
|
+
await createScopeContext(scopeId);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
159
299
|
|
|
160
300
|
const mcpEventsClient = new McpEventsClientManager({
|
|
161
|
-
store,
|
|
162
|
-
compositeEventConsumer,
|
|
301
|
+
store: defaultContext.store,
|
|
302
|
+
compositeEventConsumer: defaultContext.compositeEventConsumer,
|
|
303
|
+
resolveScope: createScopeContext,
|
|
163
304
|
connections: mcpEventConnections,
|
|
164
305
|
registerEventSource: (source) =>
|
|
165
|
-
triggerControl.registerEventSource(
|
|
306
|
+
defaultContext.triggerControl.registerEventSource(
|
|
166
307
|
source,
|
|
167
308
|
{
|
|
168
309
|
type: 'system',
|
|
@@ -176,18 +317,32 @@ export async function createLocalEventIntelligenceRuntime({
|
|
|
176
317
|
|
|
177
318
|
return {
|
|
178
319
|
restored,
|
|
179
|
-
store,
|
|
320
|
+
store: defaultContext.store,
|
|
180
321
|
evaluator,
|
|
181
|
-
triggerEngine,
|
|
182
|
-
triggerControl,
|
|
183
|
-
triggerInspector,
|
|
184
|
-
compositeEventConsumer,
|
|
185
|
-
temporalScheduler,
|
|
322
|
+
triggerEngine: defaultContext.triggerEngine,
|
|
323
|
+
triggerControl: defaultContext.triggerControl,
|
|
324
|
+
triggerInspector: defaultContext.triggerInspector,
|
|
325
|
+
compositeEventConsumer: defaultContext.compositeEventConsumer,
|
|
326
|
+
temporalScheduler: defaultContext.temporalScheduler,
|
|
186
327
|
mcpEventsClient,
|
|
187
328
|
discovery,
|
|
329
|
+
scope: createScopeContext,
|
|
330
|
+
loadedScopes() {
|
|
331
|
+
return [...scopeContexts.keys()].sort();
|
|
332
|
+
},
|
|
188
333
|
async close() {
|
|
189
|
-
|
|
190
|
-
|
|
334
|
+
await mcpEventsClient.close();
|
|
335
|
+
const contexts = [...scopeContexts.values()];
|
|
336
|
+
await Promise.all(contexts.map((context) => context.close()));
|
|
337
|
+
|
|
338
|
+
const stores = new Set(contexts.map((context) => context.store));
|
|
339
|
+
for (const store of stores) {
|
|
340
|
+
if (typeof store.close === 'function') {
|
|
341
|
+
await store.close();
|
|
342
|
+
} else if (typeof store.drain === 'function') {
|
|
343
|
+
await store.drain();
|
|
344
|
+
}
|
|
345
|
+
}
|
|
191
346
|
},
|
|
192
347
|
};
|
|
193
348
|
}
|