mcp-event-intelligence 0.3.1 → 0.5.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 (51) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +123 -23
  3. package/SECURITY.md +4 -4
  4. package/bin/mcp-event-intelligence.mjs +3 -3
  5. package/dist/src/composite/engine.d.ts +6 -0
  6. package/dist/src/composite/engine.js +599 -10
  7. package/dist/src/composite/matching.d.ts +1 -1
  8. package/dist/src/composite/matching.js +66 -1
  9. package/dist/src/intelligenceProtocol/activationSchemas.d.ts +2 -0
  10. package/dist/src/intelligenceProtocol/activationSchemas.js +1 -0
  11. package/dist/src/intelligenceProtocol/index.d.ts +9 -2
  12. package/dist/src/intelligenceProtocol/index.js +5 -1
  13. package/dist/src/intelligenceProtocol/operatorRegistry.d.ts +567 -0
  14. package/dist/src/intelligenceProtocol/operatorRegistry.js +477 -0
  15. package/dist/src/intelligenceProtocol/patternV2Schemas.d.ts +188 -0
  16. package/dist/src/intelligenceProtocol/patternV2Schemas.js +801 -0
  17. package/dist/src/intelligenceProtocol/schemas.d.ts +6 -0
  18. package/dist/src/intelligenceProtocol/schemas.js +3 -0
  19. package/dist/src/intelligenceProtocol/triggerPlanSchemas.d.ts +179 -4
  20. package/dist/src/intelligenceProtocol/triggerPlanSchemas.js +38 -4
  21. package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +262 -0
  22. package/dist/src/intelligenceProtocol/triggerSchemas.js +164 -2
  23. package/dist/src/mcpEvents/consumer.d.ts +2 -0
  24. package/dist/src/mcpEvents/consumer.js +2 -0
  25. package/dist/src/mcpEvents/erpnextProvider.d.ts +0 -2
  26. package/dist/src/mcpEvents/erpnextProvider.js +0 -3
  27. package/dist/src/mcpEvents/provider.d.ts +24 -10
  28. package/dist/src/mcpEvents/provider.js +124 -55
  29. package/dist/src/mcpEvents/server.d.ts +3 -1
  30. package/dist/src/mcpEvents/server.js +20 -19
  31. package/dist/src/patternV2/evaluator.d.ts +43 -0
  32. package/dist/src/patternV2/evaluator.js +927 -0
  33. package/dist/src/patternV2/legacyCompiler.d.ts +3 -0
  34. package/dist/src/patternV2/legacyCompiler.js +184 -0
  35. package/dist/src/protocol/types.d.ts +1 -0
  36. package/package.json +5 -2
  37. package/scripts/host-integration.d.mts +47 -0
  38. package/scripts/host-integration.mjs +9 -0
  39. package/scripts/lib/activation-hydrator.mjs +1 -0
  40. package/scripts/lib/composite-event-consumer.mjs +4 -0
  41. package/scripts/lib/derived-event-coordinator.mjs +75 -1
  42. package/scripts/lib/json-schema.mjs +134 -0
  43. package/scripts/lib/local-event-intelligence-runtime.mjs +1 -0
  44. package/scripts/lib/mcp-events-client.mjs +798 -253
  45. package/scripts/lib/mcp-events-compatibility.mjs +54 -0
  46. package/scripts/lib/persistent-event-store.mjs +68 -9
  47. package/scripts/lib/trigger-control-plane.mjs +180 -0
  48. package/scripts/lib/trigger-inspector.mjs +15 -0
  49. package/scripts/lib/trigger-planner.mjs +213 -12
  50. package/scripts/mcp-stdio-server.mjs +38 -52
  51. package/server.json +4 -4
@@ -0,0 +1,801 @@
1
+ import { z } from 'zod';
2
+ const Id = z.string().min(1).max(200);
3
+ const Scalar = z.union([z.string(), z.number(), z.boolean()]);
4
+ const ClockTime = z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/);
5
+ const CalendarDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
6
+ export const PATTERN_AST_VERSION = '2';
7
+ export const PatternValueSelectorSchema = z.discriminatedUnion('kind', [
8
+ z.object({
9
+ kind: z.literal('literal'),
10
+ value: z.union([Scalar, z.null()]),
11
+ }).strict(),
12
+ z.object({
13
+ kind: z.literal('field'),
14
+ ref: Id,
15
+ path: z.string().min(1),
16
+ select: z.enum(['first', 'last', 'nth']).default('last'),
17
+ nth: z.number().int().min(0).optional(),
18
+ }).strict().superRefine((value, ctx) => {
19
+ if (value.select === 'nth' && value.nth === undefined) {
20
+ ctx.addIssue({
21
+ code: 'custom',
22
+ message: 'field selector nth requires nth',
23
+ });
24
+ }
25
+ }),
26
+ z.object({
27
+ kind: z.literal('occurredAt'),
28
+ ref: Id,
29
+ select: z.enum(['first', 'last', 'nth']).default('last'),
30
+ nth: z.number().int().min(0).optional(),
31
+ }).strict().superRefine((value, ctx) => {
32
+ if (value.select === 'nth' && value.nth === undefined) {
33
+ ctx.addIssue({
34
+ code: 'custom',
35
+ message: 'occurredAt selector nth requires nth',
36
+ });
37
+ }
38
+ }),
39
+ ]);
40
+ export const PatternArithmeticValueSchema = z.lazy(() => z.union([
41
+ PatternValueSelectorSchema,
42
+ z.object({
43
+ kind: z.literal('arithmetic'),
44
+ op: z.enum(['add', 'subtract', 'multiply', 'divide', 'abs']),
45
+ args: z.array(PatternArithmeticValueSchema).min(1).max(8),
46
+ }).strict().superRefine((value, ctx) => {
47
+ if (value.op === 'abs' && value.args.length !== 1) {
48
+ ctx.addIssue({
49
+ code: 'custom',
50
+ message: 'abs requires exactly one argument',
51
+ });
52
+ }
53
+ if (value.op !== 'abs' &&
54
+ value.args.length < 2) {
55
+ ctx.addIssue({
56
+ code: 'custom',
57
+ message: `${value.op} requires at least two arguments`,
58
+ });
59
+ }
60
+ }),
61
+ ]));
62
+ export const PatternCompareOperatorSchema = z.enum([
63
+ 'eq',
64
+ 'neq',
65
+ 'gt',
66
+ 'gte',
67
+ 'lt',
68
+ 'lte',
69
+ 'contains',
70
+ 'startsWith',
71
+ 'endsWith',
72
+ 'regex',
73
+ 'in',
74
+ 'notIn',
75
+ 'between',
76
+ 'isNull',
77
+ 'type',
78
+ ]);
79
+ export const AggregateFunctionSchema = z.enum([
80
+ 'sum',
81
+ 'avg',
82
+ 'min',
83
+ 'max',
84
+ 'count',
85
+ 'countDistinct',
86
+ 'first',
87
+ 'last',
88
+ 'nth',
89
+ 'stddev',
90
+ 'percentile',
91
+ ]);
92
+ export const PatternWindowSchema = z.discriminatedUnion('type', [
93
+ z.object({
94
+ type: z.literal('within'),
95
+ sizeMs: z.number().int().positive(),
96
+ }).strict(),
97
+ z.object({
98
+ type: z.literal('sliding'),
99
+ sizeMs: z.number().int().positive(),
100
+ }).strict(),
101
+ z.object({
102
+ type: z.literal('tumbling'),
103
+ sizeMs: z.number().int().positive(),
104
+ offsetMs: z.number().int().nonnegative().default(0),
105
+ }).strict(),
106
+ z.object({
107
+ type: z.literal('hopping'),
108
+ sizeMs: z.number().int().positive(),
109
+ hopMs: z.number().int().positive(),
110
+ offsetMs: z.number().int().nonnegative().default(0),
111
+ }).strict(),
112
+ z.object({
113
+ type: z.literal('session'),
114
+ gapMs: z.number().int().positive(),
115
+ }).strict(),
116
+ z.object({
117
+ type: z.literal('count'),
118
+ size: z.number().int().min(1),
119
+ }).strict(),
120
+ ]);
121
+ export const PatternNodeV2Schema = z.lazy(() => {
122
+ const node = z.union([
123
+ z.object({
124
+ kind: z.literal('event'),
125
+ ref: Id,
126
+ }).strict(),
127
+ z.object({
128
+ kind: z.literal('allOf'),
129
+ children: z.array(PatternNodeV2Schema).min(2),
130
+ }).strict(),
131
+ z.object({
132
+ kind: z.literal('anyOf'),
133
+ children: z.array(PatternNodeV2Schema).min(1),
134
+ }).strict(),
135
+ z.object({
136
+ kind: z.literal('sequence'),
137
+ children: z.array(PatternNodeV2Schema).min(2),
138
+ contiguity: z.enum([
139
+ 'next',
140
+ 'followedBy',
141
+ 'followedByAny',
142
+ ]).default('followedBy'),
143
+ }).strict(),
144
+ z.object({
145
+ kind: z.literal('repeat'),
146
+ child: PatternNodeV2Schema,
147
+ min: z.number().int().min(0),
148
+ max: z.number().int().min(1).optional(),
149
+ mode: z.enum(['greedy', 'lazy']).default('greedy'),
150
+ contiguity: z.enum([
151
+ 'consecutive',
152
+ 'relaxed',
153
+ 'combinations',
154
+ ]).default('relaxed'),
155
+ }).strict().superRefine((value, ctx) => {
156
+ if (value.max !== undefined && value.max < value.min) {
157
+ ctx.addIssue({
158
+ code: 'custom',
159
+ message: 'repeat max cannot be below min',
160
+ });
161
+ }
162
+ }),
163
+ z.object({
164
+ kind: z.literal('optional'),
165
+ child: PatternNodeV2Schema,
166
+ mode: z.enum(['greedy', 'lazy']).default('greedy'),
167
+ }).strict(),
168
+ z.object({
169
+ kind: z.literal('notNext'),
170
+ child: PatternNodeV2Schema,
171
+ forbidden: PatternNodeV2Schema,
172
+ }).strict(),
173
+ z.object({
174
+ kind: z.literal('notFollowedBy'),
175
+ id: Id,
176
+ child: PatternNodeV2Schema,
177
+ forbidden: PatternNodeV2Schema,
178
+ withinMs: z.number().int().positive(),
179
+ }).strict(),
180
+ z.object({
181
+ kind: z.literal('window'),
182
+ window: PatternWindowSchema,
183
+ child: PatternNodeV2Schema,
184
+ }).strict(),
185
+ z.object({
186
+ kind: z.literal('calendar'),
187
+ child: PatternNodeV2Schema,
188
+ ref: Id,
189
+ timezone: z.string().min(1),
190
+ before: ClockTime.optional(),
191
+ after: ClockTime.optional(),
192
+ weekdays: z.array(z.number().int().min(1).max(7)).min(1).optional(),
193
+ dates: z.array(CalendarDate).min(1).optional(),
194
+ dateRange: z.object({
195
+ start: CalendarDate,
196
+ end: CalendarDate,
197
+ }).optional(),
198
+ dayOfMonth: z.array(z.number().int().min(1).max(31)).min(1).optional(),
199
+ }).strict(),
200
+ z.object({
201
+ kind: z.literal('absence'),
202
+ id: Id,
203
+ child: PatternNodeV2Schema,
204
+ ref: Id,
205
+ afterRef: Id,
206
+ forMs: z.number().int().positive().optional(),
207
+ untilLocalTime: ClockTime.optional(),
208
+ timezone: z.string().min(1).optional(),
209
+ }).strict().superRefine((value, ctx) => {
210
+ if (!value.forMs && !value.untilLocalTime) {
211
+ ctx.addIssue({
212
+ code: 'custom',
213
+ message: 'absence requires forMs or untilLocalTime',
214
+ });
215
+ }
216
+ if (value.untilLocalTime && !value.timezone) {
217
+ ctx.addIssue({
218
+ code: 'custom',
219
+ message: 'absence untilLocalTime requires timezone',
220
+ });
221
+ }
222
+ }),
223
+ z.object({
224
+ kind: z.literal('notPresent'),
225
+ child: PatternNodeV2Schema,
226
+ ref: Id,
227
+ }).strict(),
228
+ z.object({
229
+ kind: z.literal('after'),
230
+ child: PatternNodeV2Schema,
231
+ ref: Id,
232
+ afterRef: Id,
233
+ }).strict(),
234
+ z.object({
235
+ kind: z.literal('until'),
236
+ child: PatternNodeV2Schema,
237
+ ref: Id,
238
+ beforeRef: Id,
239
+ }).strict(),
240
+ z.object({
241
+ kind: z.literal('debounce'),
242
+ id: Id,
243
+ child: PatternNodeV2Schema,
244
+ ref: Id,
245
+ forMs: z.number().int().positive(),
246
+ }).strict(),
247
+ z.object({
248
+ kind: z.literal('threshold'),
249
+ child: PatternNodeV2Schema,
250
+ ref: Id,
251
+ atLeast: z.number().int().min(1),
252
+ }).strict(),
253
+ z.object({
254
+ kind: z.literal('rate'),
255
+ child: PatternNodeV2Schema,
256
+ ref: Id,
257
+ atLeast: z.number().int().min(1),
258
+ perMs: z.number().int().positive(),
259
+ }).strict(),
260
+ z.object({
261
+ kind: z.literal('distinct'),
262
+ child: PatternNodeV2Schema,
263
+ ref: Id,
264
+ path: z.string().min(1),
265
+ atLeast: z.number().int().min(1),
266
+ }).strict(),
267
+ z.object({
268
+ kind: z.literal('compare'),
269
+ child: PatternNodeV2Schema,
270
+ left: PatternArithmeticValueSchema,
271
+ op: PatternCompareOperatorSchema,
272
+ right: PatternArithmeticValueSchema.optional(),
273
+ values: z.array(z.union([Scalar, z.null()])).optional(),
274
+ lower: z.union([Scalar, z.null()]).optional(),
275
+ upper: z.union([Scalar, z.null()]).optional(),
276
+ expectedType: z.enum([
277
+ 'string',
278
+ 'number',
279
+ 'boolean',
280
+ 'null',
281
+ 'object',
282
+ 'array',
283
+ ]).optional(),
284
+ flags: z.string().max(10).optional(),
285
+ }).strict().superRefine((value, ctx) => {
286
+ if (['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'contains', 'startsWith', 'endsWith', 'regex']
287
+ .includes(value.op) &&
288
+ !value.right) {
289
+ ctx.addIssue({
290
+ code: 'custom',
291
+ message: `compare ${value.op} requires right`,
292
+ });
293
+ }
294
+ if ((value.op === 'in' || value.op === 'notIn') &&
295
+ (!value.values || value.values.length === 0)) {
296
+ ctx.addIssue({
297
+ code: 'custom',
298
+ message: `compare ${value.op} requires values`,
299
+ });
300
+ }
301
+ if (value.op === 'between' &&
302
+ (value.lower === undefined || value.upper === undefined)) {
303
+ ctx.addIssue({
304
+ code: 'custom',
305
+ message: 'compare between requires lower and upper',
306
+ });
307
+ }
308
+ if (value.op === 'type' && !value.expectedType) {
309
+ ctx.addIssue({
310
+ code: 'custom',
311
+ message: 'compare type requires expectedType',
312
+ });
313
+ }
314
+ }),
315
+ z.object({
316
+ kind: z.literal('aggregate'),
317
+ child: PatternNodeV2Schema,
318
+ function: AggregateFunctionSchema,
319
+ ref: Id,
320
+ path: z.string().min(1).optional(),
321
+ nth: z.number().int().min(0).optional(),
322
+ percentile: z.number().min(0).max(1).optional(),
323
+ op: z.enum(['eq', 'neq', 'gt', 'gte', 'lt', 'lte']),
324
+ value: z.number(),
325
+ }).strict().superRefine((value, ctx) => {
326
+ if (['sum', 'avg', 'min', 'max', 'countDistinct', 'first', 'last', 'nth', 'stddev', 'percentile']
327
+ .includes(value.function) &&
328
+ value.function !== 'count' &&
329
+ !value.path) {
330
+ ctx.addIssue({
331
+ code: 'custom',
332
+ message: `${value.function} aggregate requires path`,
333
+ });
334
+ }
335
+ if (value.function === 'nth' && value.nth === undefined) {
336
+ ctx.addIssue({
337
+ code: 'custom',
338
+ message: 'nth aggregate requires nth',
339
+ });
340
+ }
341
+ if (value.function === 'percentile' &&
342
+ value.percentile === undefined) {
343
+ ctx.addIssue({
344
+ code: 'custom',
345
+ message: 'percentile aggregate requires percentile',
346
+ });
347
+ }
348
+ }),
349
+ z.object({
350
+ kind: z.literal('state'),
351
+ child: PatternNodeV2Schema,
352
+ ref: Id,
353
+ path: z.string().min(1),
354
+ op: z.enum([
355
+ 'changed',
356
+ 'changedFrom',
357
+ 'increasedBy',
358
+ 'decreasedBy',
359
+ 'delta',
360
+ 'percentChange',
361
+ 'stableFor',
362
+ 'crossesAbove',
363
+ 'crossesBelow',
364
+ ]),
365
+ from: z.union([Scalar, z.null()]).optional(),
366
+ to: z.union([Scalar, z.null()]).optional(),
367
+ value: z.number().optional(),
368
+ comparator: z.enum(['eq', 'neq', 'gt', 'gte', 'lt', 'lte'])
369
+ .default('gte'),
370
+ forMs: z.number().int().positive().optional(),
371
+ }).strict().superRefine((value, ctx) => {
372
+ if (value.op === 'changedFrom' &&
373
+ (value.from === undefined || value.to === undefined)) {
374
+ ctx.addIssue({
375
+ code: 'custom',
376
+ message: 'changedFrom requires from and to',
377
+ });
378
+ }
379
+ if (['increasedBy', 'decreasedBy', 'delta', 'percentChange', 'crossesAbove', 'crossesBelow']
380
+ .includes(value.op) &&
381
+ value.value === undefined) {
382
+ ctx.addIssue({
383
+ code: 'custom',
384
+ message: `${value.op} requires value`,
385
+ });
386
+ }
387
+ if (value.op === 'stableFor' && value.forMs === undefined) {
388
+ ctx.addIssue({
389
+ code: 'custom',
390
+ message: 'stableFor requires forMs',
391
+ });
392
+ }
393
+ }),
394
+ z.object({
395
+ kind: z.literal('semantic'),
396
+ id: Id,
397
+ child: PatternNodeV2Schema,
398
+ refs: z.array(Id).min(1),
399
+ instruction: z.string().min(1).max(4000),
400
+ input: z.array(z.string().min(1)).min(1),
401
+ matchThreshold: z.number().min(0).max(1),
402
+ rejectThreshold: z.number().min(0).max(1),
403
+ uncertain: z.enum(['escalate', 'reject', 'match']).default('escalate'),
404
+ execution: z.object({
405
+ cache: z.boolean().default(true),
406
+ timeoutMs: z.number().int().positive().max(60000).default(5000),
407
+ onUnavailable: z.enum(['error', 'reject']).default('error'),
408
+ }).default({
409
+ cache: true,
410
+ timeoutMs: 5000,
411
+ onUnavailable: 'error',
412
+ }),
413
+ }).strict().superRefine((value, ctx) => {
414
+ if (value.rejectThreshold > value.matchThreshold) {
415
+ ctx.addIssue({
416
+ code: 'custom',
417
+ message: 'semantic rejectThreshold cannot exceed matchThreshold',
418
+ });
419
+ }
420
+ }),
421
+ ]);
422
+ return node;
423
+ });
424
+ export const PatternPartitionDimensionSchema = z.object({
425
+ key: Id,
426
+ fields: z.array(z.object({
427
+ ref: Id,
428
+ path: z.string().min(1),
429
+ }).strict()).min(1),
430
+ }).strict();
431
+ export const PatternAfterMatchSchema = z.union([
432
+ z.enum([
433
+ 'skipPastLast',
434
+ 'skipToNext',
435
+ 'keepAll',
436
+ ]),
437
+ z.object({
438
+ kind: z.enum(['skipToFirst', 'skipToLast']),
439
+ ref: Id,
440
+ }).strict(),
441
+ ]);
442
+ export const PatternSelectionSchema = z.object({
443
+ overlap: z.enum(['allow', 'disallow']).default('disallow'),
444
+ afterMatch: PatternAfterMatchSchema.default('skipPastLast'),
445
+ maxMatchesPerEvent: z.number().int().min(1).max(100).default(10),
446
+ }).strict();
447
+ export const PatternExecutionPolicySchema = z.object({
448
+ maxCandidates: z.number().int().min(1).max(10000).default(512),
449
+ maxSemanticEvaluations: z.number().int().min(0).max(1000).default(16),
450
+ maxBufferedEvents: z.number().int().min(1).max(1000000).default(10000),
451
+ }).strict();
452
+ export const PatternMeasureSchema = z.object({
453
+ key: z.string().regex(/^[A-Za-z_][A-Za-z0-9_-]{0,63}$/),
454
+ expression: z.union([
455
+ PatternArithmeticValueSchema,
456
+ z.object({
457
+ kind: z.literal('aggregate'),
458
+ function: AggregateFunctionSchema,
459
+ ref: Id,
460
+ path: z.string().min(1).optional(),
461
+ nth: z.number().int().min(0).optional(),
462
+ percentile: z.number().min(0).max(1).optional(),
463
+ }).strict().superRefine((value, ctx) => {
464
+ if (value.function !== 'count' &&
465
+ !value.path) {
466
+ ctx.addIssue({
467
+ code: 'custom',
468
+ message: `${value.function} measure requires path`,
469
+ });
470
+ }
471
+ if (value.function === 'nth' && value.nth === undefined) {
472
+ ctx.addIssue({
473
+ code: 'custom',
474
+ message: 'nth measure requires nth',
475
+ });
476
+ }
477
+ if (value.function === 'percentile' &&
478
+ value.percentile === undefined) {
479
+ ctx.addIssue({
480
+ code: 'custom',
481
+ message: 'percentile measure requires percentile',
482
+ });
483
+ }
484
+ }),
485
+ ]),
486
+ }).strict();
487
+ function valueRefs(value) {
488
+ if (value.kind === 'field' || value.kind === 'occurredAt') {
489
+ return [String(value.ref)];
490
+ }
491
+ if (value.kind === 'arithmetic') {
492
+ return [
493
+ ...new Set(value.args.flatMap((arg) => valueRefs(arg))),
494
+ ];
495
+ }
496
+ return [];
497
+ }
498
+ export function collectGuaranteedPatternBindings(node) {
499
+ const visit = (current) => {
500
+ if (current.kind === 'event')
501
+ return new Set([current.ref]);
502
+ if (current.kind === 'allOf' || current.kind === 'sequence') {
503
+ return new Set(current.children.flatMap((child) => [...visit(child)]));
504
+ }
505
+ if (current.kind === 'anyOf') {
506
+ const children = current.children.map((child) => visit(child));
507
+ if (!children.length)
508
+ return new Set();
509
+ return new Set([...children[0]].filter((ref) => children.slice(1).every((set) => set.has(ref))));
510
+ }
511
+ if (current.kind === 'repeat') {
512
+ return current.min > 0 ? visit(current.child) : new Set();
513
+ }
514
+ if (current.kind === 'optional')
515
+ return new Set();
516
+ if (current.kind === 'threshold' ||
517
+ current.kind === 'rate' ||
518
+ current.kind === 'distinct' ||
519
+ current.kind === 'debounce') {
520
+ const refs = visit(current.child);
521
+ refs.add(current.ref);
522
+ return refs;
523
+ }
524
+ if (current.kind === 'notNext' ||
525
+ current.kind === 'notFollowedBy') {
526
+ return visit(current.child);
527
+ }
528
+ return visit(current.child);
529
+ };
530
+ return [...visit(node)];
531
+ }
532
+ function validatePatternNodeBindings(node, ctx) {
533
+ const requireBindings = (refs, child, label) => {
534
+ const guaranteed = new Set(collectGuaranteedPatternBindings(child));
535
+ for (const ref of refs) {
536
+ if (!guaranteed.has(ref)) {
537
+ ctx.addIssue({
538
+ code: 'custom',
539
+ message: `${label} requires ref to be guaranteed by child pattern: ${ref}`,
540
+ });
541
+ }
542
+ }
543
+ };
544
+ if (node.kind === 'allOf' ||
545
+ node.kind === 'anyOf' ||
546
+ node.kind === 'sequence') {
547
+ node.children.forEach((child) => validatePatternNodeBindings(child, ctx));
548
+ return;
549
+ }
550
+ if (node.kind === 'repeat' ||
551
+ node.kind === 'optional' ||
552
+ node.kind === 'window' ||
553
+ node.kind === 'notPresent' ||
554
+ node.kind === 'threshold' ||
555
+ node.kind === 'rate' ||
556
+ node.kind === 'distinct' ||
557
+ node.kind === 'debounce') {
558
+ validatePatternNodeBindings(node.child, ctx);
559
+ return;
560
+ }
561
+ if (node.kind === 'notNext' ||
562
+ node.kind === 'notFollowedBy') {
563
+ validatePatternNodeBindings(node.child, ctx);
564
+ validatePatternNodeBindings(node.forbidden, ctx);
565
+ return;
566
+ }
567
+ if (node.kind === 'calendar') {
568
+ requireBindings([node.ref], node.child, 'calendar');
569
+ validatePatternNodeBindings(node.child, ctx);
570
+ return;
571
+ }
572
+ if (node.kind === 'absence') {
573
+ requireBindings([node.afterRef], node.child, 'absence');
574
+ validatePatternNodeBindings(node.child, ctx);
575
+ return;
576
+ }
577
+ if (node.kind === 'after') {
578
+ requireBindings([node.ref, node.afterRef], node.child, 'after');
579
+ validatePatternNodeBindings(node.child, ctx);
580
+ return;
581
+ }
582
+ if (node.kind === 'until') {
583
+ requireBindings([node.ref, node.beforeRef], node.child, 'until');
584
+ validatePatternNodeBindings(node.child, ctx);
585
+ return;
586
+ }
587
+ if (node.kind === 'compare') {
588
+ requireBindings([
589
+ ...valueRefs(node.left),
590
+ ...(node.right ? valueRefs(node.right) : []),
591
+ ], node.child, 'compare');
592
+ validatePatternNodeBindings(node.child, ctx);
593
+ return;
594
+ }
595
+ if (node.kind === 'aggregate' || node.kind === 'state') {
596
+ requireBindings([node.ref], node.child, node.kind);
597
+ validatePatternNodeBindings(node.child, ctx);
598
+ return;
599
+ }
600
+ if (node.kind === 'semantic') {
601
+ requireBindings(node.refs, node.child, 'semantic');
602
+ const allowed = new Set(node.refs);
603
+ for (const input of node.input) {
604
+ const [root] = input.split('.');
605
+ if (!root || !allowed.has(root)) {
606
+ ctx.addIssue({
607
+ code: 'custom',
608
+ message: `semantic input must be rooted in semantic refs: ${input}`,
609
+ });
610
+ }
611
+ }
612
+ validatePatternNodeBindings(node.child, ctx);
613
+ }
614
+ }
615
+ export const PatternAstV2DefinitionSchema = z.object({
616
+ version: z.literal(PATTERN_AST_VERSION).default(PATTERN_AST_VERSION),
617
+ root: PatternNodeV2Schema,
618
+ partitionBy: z.array(PatternPartitionDimensionSchema).default([]),
619
+ selection: PatternSelectionSchema.default({
620
+ overlap: 'disallow',
621
+ afterMatch: 'skipPastLast',
622
+ maxMatchesPerEvent: 10,
623
+ }),
624
+ execution: PatternExecutionPolicySchema.default({
625
+ maxCandidates: 512,
626
+ maxSemanticEvaluations: 16,
627
+ maxBufferedEvents: 10000,
628
+ }),
629
+ }).strict().superRefine((value, ctx) => {
630
+ validatePatternNodeBindings(value.root, ctx);
631
+ });
632
+ export function collectPatternMeasureRefs(measure) {
633
+ const refs = new Set();
634
+ const visitValue = (value) => {
635
+ if (value.kind === 'field' || value.kind === 'occurredAt') {
636
+ refs.add(value.ref);
637
+ return;
638
+ }
639
+ if (value.kind === 'arithmetic') {
640
+ value.args.forEach((arg) => visitValue(arg));
641
+ }
642
+ };
643
+ if (measure.expression.kind === 'aggregate') {
644
+ refs.add(measure.expression.ref);
645
+ }
646
+ else {
647
+ visitValue(measure.expression);
648
+ }
649
+ return [...refs];
650
+ }
651
+ export function collectPatternDurationsMs(node) {
652
+ const durations = [];
653
+ const visit = (current) => {
654
+ if (current.kind === 'window') {
655
+ if (current.window.type === 'within' ||
656
+ current.window.type === 'sliding' ||
657
+ current.window.type === 'tumbling' ||
658
+ current.window.type === 'hopping') {
659
+ durations.push(current.window.sizeMs);
660
+ }
661
+ else if (current.window.type === 'session') {
662
+ durations.push(current.window.gapMs);
663
+ }
664
+ visit(current.child);
665
+ return;
666
+ }
667
+ if (current.kind === 'notFollowedBy') {
668
+ durations.push(current.withinMs);
669
+ visit(current.child);
670
+ visit(current.forbidden);
671
+ return;
672
+ }
673
+ if (current.kind === 'absence') {
674
+ if (current.forMs)
675
+ durations.push(current.forMs);
676
+ visit(current.child);
677
+ return;
678
+ }
679
+ if (current.kind === 'debounce') {
680
+ durations.push(current.forMs);
681
+ visit(current.child);
682
+ return;
683
+ }
684
+ if (current.kind === 'rate') {
685
+ durations.push(current.perMs);
686
+ visit(current.child);
687
+ return;
688
+ }
689
+ if (current.kind === 'state') {
690
+ if (current.forMs)
691
+ durations.push(current.forMs);
692
+ visit(current.child);
693
+ return;
694
+ }
695
+ if (current.kind === 'repeat' ||
696
+ current.kind === 'optional' ||
697
+ current.kind === 'calendar' ||
698
+ current.kind === 'notPresent' ||
699
+ current.kind === 'after' ||
700
+ current.kind === 'until' ||
701
+ current.kind === 'threshold' ||
702
+ current.kind === 'distinct' ||
703
+ current.kind === 'compare' ||
704
+ current.kind === 'aggregate' ||
705
+ current.kind === 'semantic') {
706
+ visit(current.child);
707
+ return;
708
+ }
709
+ if (current.kind === 'notNext') {
710
+ visit(current.child);
711
+ visit(current.forbidden);
712
+ return;
713
+ }
714
+ if (current.kind === 'allOf' ||
715
+ current.kind === 'anyOf' ||
716
+ current.kind === 'sequence') {
717
+ current.children.forEach(visit);
718
+ }
719
+ };
720
+ visit(node);
721
+ return durations;
722
+ }
723
+ export function collectPatternRefs(node) {
724
+ const refs = new Set();
725
+ const visitValue = (value) => {
726
+ if (value.kind === 'field' || value.kind === 'occurredAt') {
727
+ refs.add(value.ref);
728
+ return;
729
+ }
730
+ if (value.kind === 'arithmetic') {
731
+ for (const arg of value.args)
732
+ visitValue(arg);
733
+ }
734
+ };
735
+ const visit = (current) => {
736
+ switch (current.kind) {
737
+ case 'event':
738
+ refs.add(current.ref);
739
+ return;
740
+ case 'allOf':
741
+ case 'anyOf':
742
+ case 'sequence':
743
+ current.children.forEach(visit);
744
+ return;
745
+ case 'repeat':
746
+ case 'optional':
747
+ case 'window':
748
+ visit(current.child);
749
+ return;
750
+ case 'calendar':
751
+ case 'notPresent':
752
+ case 'threshold':
753
+ case 'rate':
754
+ case 'distinct':
755
+ visit(current.child);
756
+ refs.add(current.ref);
757
+ return;
758
+ case 'absence':
759
+ visit(current.child);
760
+ refs.add(current.ref);
761
+ refs.add(current.afterRef);
762
+ return;
763
+ case 'after':
764
+ visit(current.child);
765
+ refs.add(current.ref);
766
+ refs.add(current.afterRef);
767
+ return;
768
+ case 'until':
769
+ visit(current.child);
770
+ refs.add(current.ref);
771
+ refs.add(current.beforeRef);
772
+ return;
773
+ case 'debounce':
774
+ visit(current.child);
775
+ refs.add(current.ref);
776
+ return;
777
+ case 'notNext':
778
+ case 'notFollowedBy':
779
+ visit(current.child);
780
+ visit(current.forbidden);
781
+ return;
782
+ case 'compare':
783
+ visit(current.child);
784
+ visitValue(current.left);
785
+ if (current.right)
786
+ visitValue(current.right);
787
+ return;
788
+ case 'aggregate':
789
+ case 'state':
790
+ visit(current.child);
791
+ refs.add(current.ref);
792
+ return;
793
+ case 'semantic':
794
+ visit(current.child);
795
+ current.refs.forEach((ref) => refs.add(ref));
796
+ return;
797
+ }
798
+ };
799
+ visit(node);
800
+ return [...refs];
801
+ }