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.
Files changed (31) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +79 -4
  3. package/bin/mcp-event-intelligence.mjs +1 -1
  4. package/dist/src/composite/derivedGraph.d.ts +4 -0
  5. package/dist/src/composite/derivedGraph.js +46 -0
  6. package/dist/src/composite/engine.d.ts +3 -64
  7. package/dist/src/composite/engine.js +26 -343
  8. package/dist/src/composite/matching.d.ts +8 -0
  9. package/dist/src/composite/matching.js +109 -0
  10. package/dist/src/composite/runtimeSupport.d.ts +24 -0
  11. package/dist/src/composite/runtimeSupport.js +176 -0
  12. package/dist/src/composite/store.d.ts +59 -0
  13. package/dist/src/composite/store.js +1 -0
  14. package/dist/src/intelligenceProtocol/schemas.d.ts +9 -7
  15. package/dist/src/intelligenceProtocol/schemas.js +1 -0
  16. package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +6 -6
  17. package/dist/src/mcpEvents/erpnextProvider.d.ts +20 -0
  18. package/dist/src/mcpEvents/erpnextProvider.js +80 -0
  19. package/package.json +5 -1
  20. package/scripts/host-integration.d.mts +34 -4
  21. package/scripts/host-integration.mjs +44 -1
  22. package/scripts/lib/composite-wake-coordinator.mjs +233 -9
  23. package/scripts/lib/event-processor.mjs +235 -20
  24. package/scripts/lib/local-event-intelligence-runtime.mjs +238 -83
  25. package/scripts/lib/mcp-events-client.mjs +184 -66
  26. package/scripts/lib/persistent-event-store.mjs +320 -1
  27. package/scripts/lib/temporal-deadline-scheduler.mjs +12 -2
  28. package/scripts/lib/wake-retry-scheduler.mjs +104 -0
  29. package/scripts/mcp-stdio-server.mjs +1 -1
  30. package/scripts/service.mjs +20 -0
  31. 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 store = new PersistentEventStore(dataDir);
46
- const restored = await store.init();
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
- wakeCoordinators.set(
84
- runtime,
85
- new CompositeWakeCoordinator({
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: buildGenericRuntimeWakePacket,
214
+ packetBuilder,
215
+ ...deliveryOptions,
89
216
  deliverer: async (packet) => {
90
- const result = await handler(packet);
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
- wakeCoordinator = new CompositeWakeCoordinator({
233
+
234
+ const derivedEventCoordinator = new DerivedEventCoordinator({ store });
235
+ const compositeEventConsumer = new CompositeEventConsumer({
116
236
  store,
117
237
  triggerEngine,
118
- packetBuilder: buildGenericRuntimeWakePacket,
119
- deliverer: async (packet) => {
120
- const result = await wake(packet);
121
- if (typeof result === 'string' && result) {
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
- const derivedEventCoordinator = new DerivedEventCoordinator({ store });
138
- const compositeEventConsumer = new CompositeEventConsumer({
139
- store,
140
- triggerEngine,
141
- wakeCoordinator,
142
- wakeCoordinators,
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
- const temporalScheduler = new TemporalDeadlineScheduler({
148
- store,
149
- compositeEventConsumer,
150
- intervalMs: Number(env.TEMPORAL_TICK_MS ?? 1000),
151
- });
152
- temporalScheduler.start();
251
+ const triggerControl = new TriggerControlPlane({
252
+ store,
253
+ triggerEngine,
254
+ });
255
+ const triggerInspector = new TriggerInspector({ store });
153
256
 
154
- const triggerControl = new TriggerControlPlane({
155
- store,
156
- triggerEngine,
157
- });
158
- const triggerInspector = new TriggerInspector({ store });
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
- temporalScheduler.stop();
190
- mcpEventsClient.stop();
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
  }