mcp-event-intelligence 0.4.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 (64) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/README.md +107 -26
  3. package/SECURITY.md +4 -4
  4. package/bin/mcp-event-intelligence.mjs +3 -3
  5. package/dist/src/composite/engine.d.ts +14 -4
  6. package/dist/src/composite/engine.js +533 -208
  7. package/dist/src/composite/matching.d.ts +1 -5
  8. package/dist/src/composite/matching.js +45 -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 +38 -3
  14. package/dist/src/intelligenceProtocol/activationSchemas.js +5 -3
  15. package/dist/src/intelligenceProtocol/index.d.ts +12 -9
  16. package/dist/src/intelligenceProtocol/index.js +6 -5
  17. package/dist/src/intelligenceProtocol/lifecycle.js +2 -10
  18. package/dist/src/intelligenceProtocol/operatorRegistry.d.ts +300 -0
  19. package/dist/src/intelligenceProtocol/operatorRegistry.js +338 -0
  20. package/dist/src/intelligenceProtocol/patternV2Schemas.d.ts +188 -0
  21. package/dist/src/intelligenceProtocol/patternV2Schemas.js +801 -0
  22. package/dist/src/intelligenceProtocol/schemas.d.ts +4 -189
  23. package/dist/src/intelligenceProtocol/schemas.js +3 -97
  24. package/dist/src/intelligenceProtocol/triggerPlanSchemas.d.ts +114 -38
  25. package/dist/src/intelligenceProtocol/triggerPlanSchemas.js +27 -18
  26. package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +268 -213
  27. package/dist/src/intelligenceProtocol/triggerSchemas.js +155 -169
  28. package/dist/src/intelligenceProtocol/version.d.ts +2 -2
  29. package/dist/src/intelligenceProtocol/version.js +2 -2
  30. package/dist/src/mcpEvents/consumer.d.ts +1 -0
  31. package/dist/src/mcpEvents/consumer.js +1 -0
  32. package/dist/src/patternV2/evaluator.d.ts +51 -0
  33. package/dist/src/patternV2/evaluator.js +986 -0
  34. package/dist/src/semantic/types.d.ts +6 -0
  35. package/dist/src/semantic/typesafeJevEvaluator.d.ts +1 -0
  36. package/dist/src/semantic/typesafeJevEvaluator.js +6 -0
  37. package/package.json +9 -3
  38. package/scripts/host-integration.d.mts +54 -4
  39. package/scripts/host-integration.mjs +25 -4
  40. package/scripts/lib/activation-hydrator.mjs +13 -12
  41. package/scripts/lib/composite-event-consumer.mjs +10 -28
  42. package/scripts/lib/composite-wake-coordinator.mjs +19 -19
  43. package/scripts/lib/derived-contract-registry.mjs +4 -4
  44. package/scripts/lib/derived-event-coordinator.mjs +69 -6
  45. package/scripts/lib/local-event-intelligence-runtime.mjs +35 -3
  46. package/scripts/lib/mcp-events-client.mjs +97 -55
  47. package/scripts/lib/mcp-events-compatibility.mjs +54 -0
  48. package/scripts/lib/persistent-event-store.mjs +228 -61
  49. package/scripts/lib/store-capabilities.mjs +88 -0
  50. package/scripts/lib/store-conformance.mjs +127 -0
  51. package/scripts/lib/temporal-deadline-scheduler.mjs +3 -3
  52. package/scripts/lib/trigger-control-plane.mjs +202 -53
  53. package/scripts/lib/trigger-inspector.mjs +50 -147
  54. package/scripts/lib/trigger-planner.mjs +141 -52
  55. package/scripts/lib/wake-retry-scheduler.mjs +2 -19
  56. package/scripts/mcp-stdio-server.mjs +46 -61
  57. package/scripts/service.mjs +19 -138
  58. package/server.json +4 -4
  59. package/dist/src/intelligenceProtocol/invariants.d.ts +0 -3
  60. package/dist/src/intelligenceProtocol/invariants.js +0 -36
  61. package/dist/src/intelligenceProtocol/mapper.d.ts +0 -14
  62. package/dist/src/intelligenceProtocol/mapper.js +0 -28
  63. package/scripts/lib/event-processor.mjs +0 -624
  64. package/scripts/lib/http-wake-deliverer.mjs +0 -31
@@ -1,9 +1,6 @@
1
1
  import {
2
2
  CompositeTriggerEngine,
3
3
  } from '../../dist/src/composite/engine.js';
4
- import {
5
- evaluateTemporalConditions,
6
- } from '../../dist/src/composite/temporal.js';
7
4
  import {
8
5
  parseCompositeTriggerDefinition,
9
6
  parseCorrelatableEvent,
@@ -11,8 +8,19 @@ import {
11
8
  } from '../../dist/src/intelligenceProtocol/index.js';
12
9
 
13
10
  function latest(records) {
11
+ const rank = (record) => {
12
+ if (record.status === 'fired') return 5;
13
+ if (record.status === 'emitted') return 4;
14
+ if (record.status === 'matched') return 3;
15
+ if (record.status === 'partial' && record.patternState?.role === 'match') return 2;
16
+ if (record.status === 'partial') return 1;
17
+ return 0;
18
+ };
19
+
14
20
  return [...records].sort(
15
- (a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt),
21
+ (a, b) =>
22
+ Date.parse(b.updatedAt) - Date.parse(a.updatedAt) ||
23
+ rank(b) - rank(a),
16
24
  )[0] ?? null;
17
25
  }
18
26
 
@@ -32,12 +40,6 @@ function sourceRef(event) {
32
40
  };
33
41
  }
34
42
 
35
- function baseExpressionRefs(expression) {
36
- return expression.kind === 'count'
37
- ? [expression.ref]
38
- : expression.refs;
39
- }
40
-
41
43
  function clauseState(definition, match) {
42
44
  const seen = new Map();
43
45
  for (const event of match?.sourceEvents ?? []) {
@@ -48,63 +50,19 @@ function clauseState(definition, match) {
48
50
 
49
51
  return definition.clauses.map((clause) => {
50
52
  const evidence = seen.get(clause.id) ?? [];
51
- const requiredCount =
52
- definition.expression.kind === 'count' &&
53
- definition.expression.ref === clause.id
54
- ? definition.expression.atLeast
55
- : 1;
56
53
  return {
57
54
  clauseId: clause.id,
58
55
  event: clause.event,
59
56
  serverId: clause.serverId ?? null,
60
57
  contractVersion: clause.contractVersion ?? null,
61
- status: evidence.length >= requiredCount ? 'satisfied' : 'waiting',
58
+ status: evidence.length ? 'observed' : 'waiting',
62
59
  observedCount: evidence.length,
63
- requiredCount,
64
60
  evidence,
65
61
  };
66
62
  });
67
63
  }
68
64
 
69
- function baseExpressionSatisfied(definition, clauses) {
70
- const byId = new Map(clauses.map((entry) => [entry.clauseId, entry]));
71
- const expression = definition.expression;
72
- if (expression.kind === 'allOf') {
73
- return expression.refs.every(
74
- (ref) => byId.get(ref)?.status === 'satisfied',
75
- );
76
- }
77
- if (expression.kind === 'anyOf') {
78
- return expression.refs.some(
79
- (ref) => byId.get(ref)?.status === 'satisfied',
80
- );
81
- }
82
- if (expression.kind === 'count') {
83
- return (byId.get(expression.ref)?.observedCount ?? 0) >= expression.atLeast;
84
- }
85
-
86
- const ordered = [];
87
- for (const ref of expression.refs) {
88
- const evidence = byId.get(ref)?.evidence ?? [];
89
- if (!evidence.length) return false;
90
- ordered.push(evidence[0]);
91
- }
92
- return ordered.every(
93
- (event, index) =>
94
- index === 0 ||
95
- Date.parse(event.occurredAt) >
96
- Date.parse(ordered[index - 1].occurredAt),
97
- );
98
- }
99
-
100
- function explain({
101
- definition,
102
- state,
103
- match,
104
- clauses,
105
- temporal,
106
- wake,
107
- }) {
65
+ function explain({ state, match, wake }) {
108
66
  if (state.status === 'paused') {
109
67
  return {
110
68
  code: 'trigger_paused',
@@ -132,8 +90,9 @@ function explain({
132
90
 
133
91
  if (!match) {
134
92
  return {
135
- code: 'waiting_for_first_event',
136
- summary: `Waiting for: ${baseExpressionRefs(definition.expression).join(', ')}.`,
93
+ code: 'waiting_for_pattern_evidence',
94
+ summary: 'Waiting for evidence that can satisfy the Pattern AST.',
95
+ patternVersion: '2',
137
96
  };
138
97
  }
139
98
 
@@ -156,53 +115,22 @@ function explain({
156
115
  if (match.status === 'matched') {
157
116
  return {
158
117
  code: 'matched_waiting_delivery',
159
- summary: 'All trigger conditions are satisfied; runtime delivery is pending or not yet marked fired.',
118
+ summary: 'The Pattern AST is satisfied; effect delivery is pending or not yet marked fired.',
160
119
  };
161
120
  }
162
121
  if (match.status === 'expired') {
163
122
  return {
164
123
  code: 'match_expired',
165
- summary: 'This match can no longer satisfy the trigger.',
166
- };
167
- }
168
-
169
- const missing = clauses
170
- .filter((clause) => clause.status !== 'satisfied')
171
- .map((clause) => clause.clauseId);
172
-
173
- if (!baseExpressionSatisfied(definition, clauses)) {
174
- return {
175
- code: 'waiting_for_event_clauses',
176
- summary: `Waiting for event clauses: ${missing.join(', ') || 'expression ordering'}.`,
177
- missingClauses: missing,
178
- };
179
- }
180
-
181
- const blocked = temporal.conditionStates.filter(
182
- (condition) => condition.status === 'blocked',
183
- );
184
- if (blocked.length) {
185
- return {
186
- code: 'temporal_blocked',
187
- summary: `Temporal condition blocked: ${blocked.map((entry) => entry.conditionId).join(', ')}.`,
188
- temporalConditionIds: blocked.map((entry) => entry.conditionId),
189
- };
190
- }
191
-
192
- const pending = temporal.conditionStates.filter(
193
- (condition) => condition.status === 'pending',
194
- );
195
- if (pending.length) {
196
- return {
197
- code: 'waiting_for_temporal_conditions',
198
- summary: `Waiting for temporal conditions: ${pending.map((entry) => entry.conditionId).join(', ')}.`,
199
- temporalConditionIds: pending.map((entry) => entry.conditionId),
124
+ summary: 'This Pattern match can no longer satisfy the trigger.',
200
125
  };
201
126
  }
202
127
 
203
128
  return {
204
- code: 'waiting_for_correlation',
205
- summary: 'Event and temporal conditions are present; correlation or semantic evaluation has not completed.',
129
+ code: 'pattern_waiting',
130
+ summary: 'Pattern AST has buffered evidence but no new complete match is currently eligible.',
131
+ patternVersion: '2',
132
+ bufferRole: match.patternState?.role ?? null,
133
+ semanticDecisions: match.patternState?.semanticDecisions ?? [],
206
134
  };
207
135
  }
208
136
 
@@ -215,12 +143,12 @@ export class TriggerInspector {
215
143
  this.now = now;
216
144
  }
217
145
 
218
- inspect({
146
+ async inspect({
219
147
  triggerId,
220
148
  version,
221
149
  matchId,
222
150
  }) {
223
- const candidates = this.store.listTriggers()
151
+ const candidates = (await this.store.listTriggers())
224
152
  .filter((definition) => definition.triggerId === triggerId)
225
153
  .filter((definition) => !version || definition.version === version);
226
154
 
@@ -238,12 +166,12 @@ export class TriggerInspector {
238
166
  })
239
167
  )[0];
240
168
 
241
- const state = this.store.getTriggerState(
169
+ const state = await this.store.getTriggerState(
242
170
  definition.triggerId,
243
171
  definition.version,
244
172
  );
245
173
 
246
- const matches = this.store.listTriggerMatches(definition.triggerId)
174
+ const matches = (await this.store.listTriggerMatches(definition.triggerId))
247
175
  .filter((match) => match.triggerVersion === definition.version);
248
176
  const match = matchId
249
177
  ? matches.find((candidate) => candidate.matchId === matchId) ?? null
@@ -256,23 +184,8 @@ export class TriggerInspector {
256
184
  }
257
185
 
258
186
  const clauses = clauseState(definition, match);
259
- const temporal = match
260
- ? evaluateTemporalConditions(definition, match, this.now())
261
- : {
262
- outcome: definition.temporal?.length ? 'pending' : 'satisfied',
263
- pendingDeadlines: [],
264
- conditionStates: (definition.temporal ?? []).map((condition) => ({
265
- conditionId: condition.id,
266
- kind: condition.kind,
267
- status: 'pending',
268
- reason: 'waiting for a match',
269
- })),
270
- };
271
-
272
- const deadlines = match
273
- ? this.store.listTemporalDeadlines?.({
274
- matchId: match.matchId,
275
- }) ?? []
187
+ const deadlines = match && this.store.listTemporalDeadlines
188
+ ? await this.store.listTemporalDeadlines({ matchId: match.matchId })
276
189
  : [];
277
190
  const pendingDeadlines = deadlines.filter(
278
191
  (deadline) => deadline.status === 'pending',
@@ -282,25 +195,25 @@ export class TriggerInspector {
282
195
  .sort()[0] ?? null;
283
196
 
284
197
  const wake = match?.firedWakeId
285
- ? this.store.latestWake(match.firedWakeId)
198
+ ? await this.store.latestWake(match.firedWakeId)
286
199
  : null;
287
200
 
288
201
  const derivedOutputs = match
289
- ? (this.store.listDerivedEvents?.({ matchId: match.matchId }) ?? [])
202
+ ? (await this.store.listDerivedEvents?.({ matchId: match.matchId }) ?? [])
290
203
  : [];
291
204
 
292
- const matchHistory = match
293
- ? (this.store.listTriggerMatchHistory?.(match.matchId) ?? [match])
294
- .map((record) => ({
295
- status: record.status,
296
- updatedAt: record.updatedAt,
297
- firedWakeId: record.firedWakeId,
298
- derivedEventIds: record.derivedEventIds ?? [],
299
- sourceEventIds: record.sourceEvents.map(
300
- (event) => event.sourceEventId,
301
- ),
302
- }))
303
- : [];
205
+ const rawHistory = match && this.store.listTriggerMatchHistory
206
+ ? await this.store.listTriggerMatchHistory(match.matchId)
207
+ : (match ? [match] : []);
208
+ const matchHistory = rawHistory.map((record) => ({
209
+ status: record.status,
210
+ updatedAt: record.updatedAt,
211
+ firedWakeId: record.firedWakeId,
212
+ derivedEventIds: record.derivedEventIds ?? [],
213
+ sourceEventIds: record.sourceEvents.map(
214
+ (event) => event.sourceEventId,
215
+ ),
216
+ }));
304
217
 
305
218
  return {
306
219
  trigger: {
@@ -310,8 +223,7 @@ export class TriggerInspector {
310
223
  continuation: definition.continuation ?? null,
311
224
  target: definition.target ?? null,
312
225
  derivedEvent: definition.derivedEvent ?? null,
313
- expression: definition.expression,
314
- temporal: definition.temporal ?? [],
226
+ pattern: definition.pattern,
315
227
  lifecycle: definition.lifecycle ?? {},
316
228
  },
317
229
  lifecycle: state,
@@ -319,17 +231,14 @@ export class TriggerInspector {
319
231
  ? {
320
232
  matchId: match.matchId,
321
233
  status: match.status,
322
- correlationKey: match.correlationKey,
234
+ partitionKey: match.partitionKey,
323
235
  openedAt: match.openedAt,
324
236
  expiresAt: match.expiresAt,
325
237
  updatedAt: match.updatedAt,
238
+ patternState: match.patternState ?? null,
326
239
  }
327
240
  : null,
328
241
  clauses,
329
- temporal: {
330
- outcome: temporal.outcome,
331
- conditions: temporal.conditionStates,
332
- },
333
242
  deadlines: deadlines.map((deadline) => ({
334
243
  deadlineId: deadline.deadlineId,
335
244
  conditionId: deadline.conditionId,
@@ -360,15 +269,9 @@ export class TriggerInspector {
360
269
  rootEvidence: record.rootEvidence,
361
270
  })),
362
271
  matchHistory,
272
+ patternState: match?.patternState ?? null,
363
273
  },
364
- why: explain({
365
- definition,
366
- state,
367
- match,
368
- clauses,
369
- temporal,
370
- wake,
371
- }),
274
+ why: explain({ state, match, wake }),
372
275
  };
373
276
  }
374
277
  }
@@ -623,7 +526,7 @@ export async function simulateTrigger({
623
526
  order,
624
527
  evaluatedUntil: clock.toISOString(),
625
528
  steps,
626
- inspection: inspector.inspect({
529
+ inspection: await inspector.inspect({
627
530
  triggerId: definition.triggerId,
628
531
  version: definition.version,
629
532
  }),
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  canonicalJson,
3
+ collectPatternRefs,
3
4
  parseCompositeTriggerDefinition,
4
5
  parseTriggerPlanInput,
5
6
  sha256Hex,
@@ -88,32 +89,60 @@ function validatePredicateAgainstSource(source, predicate, warnings) {
88
89
  [source],
89
90
  );
90
91
  }
92
+
93
+ if (
94
+ ['startsWith', 'endsWith', 'regex'].includes(predicate.op) &&
95
+ node?.type &&
96
+ node.type !== 'string'
97
+ ) {
98
+ throw sourceError(
99
+ 'TRIGGER_PLAN_STRING_FIELD_REQUIRED',
100
+ `Predicate ${predicate.op} requires a string field, but ${predicate.path} is ${node.type}`,
101
+ [source],
102
+ );
103
+ }
91
104
  }
92
105
 
93
- function expressionFor(match, refs) {
94
- if (typeof match === 'object' && match?.kind === 'count') {
95
- if (!refs.includes(match.eventId)) {
96
- const error = new Error(`Count match references unknown event id: ${match.eventId}`);
97
- error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
98
- throw error;
99
- }
100
- return { kind: 'count', ref: match.eventId, atLeast: match.atLeast };
106
+ function walkPatternValue(value, visitor) {
107
+ if (!value || typeof value !== 'object') return;
108
+ if (value.kind === 'field') {
109
+ visitor(value.ref, value.path, 'pattern value');
110
+ return;
111
+ }
112
+ if (value.kind === 'arithmetic') {
113
+ for (const arg of value.args ?? []) walkPatternValue(arg, visitor);
101
114
  }
115
+ }
116
+
117
+ function walkPatternNode(node, visitor) {
118
+ if (!node || typeof node !== 'object') return;
102
119
 
103
- if (match === 'sequence') {
104
- if (refs.length < 2) {
105
- const error = new Error('Sequence match requires at least two events');
106
- error.code = 'TRIGGER_PLAN_SEQUENCE_REQUIRES_MULTIPLE_EVENTS';
107
- throw error;
120
+ if (node.kind === 'compare') {
121
+ walkPatternValue(node.left, visitor);
122
+ if (node.right) walkPatternValue(node.right, visitor);
123
+ }
124
+ if (
125
+ (node.kind === 'aggregate' ||
126
+ node.kind === 'state' ||
127
+ node.kind === 'distinct') &&
128
+ node.path
129
+ ) {
130
+ visitor(node.ref, node.path, `pattern ${node.kind}`);
131
+ }
132
+ if (node.kind === 'semantic') {
133
+ for (const input of node.input ?? []) {
134
+ const [ref, ...parts] = String(input).split('.');
135
+ if (ref && parts.length) {
136
+ visitor(ref, parts.join('.'), 'pattern semantic input');
137
+ }
108
138
  }
109
- return { kind: 'sequence', refs };
110
139
  }
111
140
 
112
- if (match === 'any' || refs.length === 1) {
113
- return { kind: 'anyOf', refs };
141
+ if (Array.isArray(node.children)) {
142
+ for (const child of node.children) walkPatternNode(child, visitor);
114
143
  }
115
-
116
- return { kind: 'allOf', refs };
144
+ if (node.child) walkPatternNode(node.child, visitor);
145
+ if (node.forbidden) walkPatternNode(node.forbidden, visitor);
117
146
  }
118
147
 
119
148
  function lifecycleFor(input = {}) {
@@ -127,6 +156,31 @@ function lifecycleFor(input = {}) {
127
156
  };
128
157
  }
129
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
+
130
184
  export class TriggerPlanner {
131
185
  constructor({ store }) {
132
186
  this.store = store;
@@ -134,7 +188,7 @@ export class TriggerPlanner {
134
188
 
135
189
  async plan(input) {
136
190
  const plan = parseTriggerPlanInput(input);
137
- const activeSources = this.store.listEventSources({ enabledOnly: true });
191
+ const activeSources = await this.store.listEventSources({ enabledOnly: true });
138
192
  const warnings = [];
139
193
  const resolvedSources = [];
140
194
  const seenIds = new Set();
@@ -185,69 +239,101 @@ export class TriggerPlanner {
185
239
  });
186
240
 
187
241
  const refs = clauses.map((clause) => clause.id);
188
- const correlation = {};
189
- if (plan.correlateBy) {
190
- for (const field of plan.correlateBy) {
191
- if (!refs.includes(field.eventId)) {
242
+
243
+ const pattern = plan.pattern ?? defaultPattern(refs);
244
+ {
245
+ const patternRefs = collectPatternRefs(pattern.root);
246
+ for (const ref of patternRefs) {
247
+ if (!refs.includes(ref)) {
192
248
  const error = new Error(
193
- `Correlation references unknown event id: ${field.eventId}`,
249
+ `Pattern AST references unknown event id: ${ref}`,
194
250
  );
195
251
  error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
196
252
  throw error;
197
253
  }
198
- const clause = clauses.find((candidate) => candidate.id === field.eventId);
199
- const source = resolvedSources[clauses.indexOf(clause)];
254
+ }
255
+
256
+ const sourceForRef = (ref) => {
257
+ const index = clauses.findIndex((candidate) => candidate.id === ref);
258
+ if (index < 0) {
259
+ const error = new Error(
260
+ `Pattern AST references unknown event id: ${ref}`,
261
+ );
262
+ error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
263
+ throw error;
264
+ }
265
+ return resolvedSources[index];
266
+ };
267
+
268
+ walkPatternNode(pattern.root, (ref, fieldPath, purpose) => {
200
269
  validatePredicateAgainstSource(
201
- source,
202
- { path: field.path, op: 'exists', value: true },
270
+ sourceForRef(ref),
271
+ { path: fieldPath, op: 'exists', value: true },
203
272
  warnings,
204
273
  );
274
+ });
275
+
276
+ for (const dimension of pattern.partitionBy) {
277
+ for (const field of dimension.fields) {
278
+ const clauseIndex = clauses.findIndex(
279
+ (candidate) => candidate.id === field.ref,
280
+ );
281
+ if (clauseIndex < 0) {
282
+ const error = new Error(
283
+ `Pattern partition references unknown event id: ${field.ref}`,
284
+ );
285
+ error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
286
+ throw error;
287
+ }
288
+ validatePredicateAgainstSource(
289
+ resolvedSources[clauseIndex],
290
+ { path: field.path, op: 'exists', value: true },
291
+ warnings,
292
+ );
293
+ }
205
294
  }
206
- correlation.deterministic = {
207
- kind: 'same_value',
208
- fields: plan.correlateBy.map((field) => ({
209
- ref: field.eventId,
210
- path: field.path,
211
- })),
212
- };
213
- }
214
- if (plan.semanticCorrelation) {
215
- correlation.semantic = plan.semanticCorrelation;
216
295
  }
217
296
 
297
+
218
298
  const triggerId = plan.triggerId || `planned_${(
219
299
  await sha256Hex(canonicalJson({
220
- target: plan.target,
300
+ target: plan.target ?? null,
221
301
  events: plan.events,
222
- match: plan.match,
223
- continuation: plan.continuation.instruction,
302
+ pattern,
303
+ eventTime: plan.eventTime ?? null,
304
+ conditionOnly: plan.conditionOnly,
305
+ continuation: plan.continuation?.instruction ?? null,
224
306
  }))
225
307
  ).slice(0, 24)}`;
226
308
 
227
309
  const definition = parseCompositeTriggerDefinition({
228
310
  triggerId,
229
311
  version: plan.version,
230
- description:
231
- plan.description ||
232
- plan.continuation.instruction.slice(0, 500),
233
- continuation: plan.continuation,
312
+ ...(plan.description || plan.continuation?.instruction
313
+ ? {
314
+ description:
315
+ plan.description ||
316
+ plan.continuation.instruction.slice(0, 500),
317
+ }
318
+ : {}),
319
+ conditionOnly: plan.conditionOnly,
320
+ ...(plan.continuation ? { continuation: plan.continuation } : {}),
234
321
  clauses,
235
- expression: expressionFor(plan.match, refs),
322
+ pattern,
236
323
  withinMs: plan.withinMs,
324
+ ...(plan.eventTime ? { eventTime: plan.eventTime } : {}),
237
325
  lifecycle: lifecycleFor(plan.lifecycle),
238
- ...(Object.keys(correlation).length ? { correlation } : {}),
239
- target: plan.target,
326
+ ...(plan.target ? { target: plan.target } : {}),
240
327
  });
241
328
 
242
329
  return {
243
- planVersion: '1',
330
+ planVersion: '2',
244
331
  definition,
245
332
  connectionIds: [...new Set(resolvedSources.map((source) => source.connectionId))],
246
333
  resolvedSources: resolvedSources.map(sourceSummary),
247
334
  warnings,
248
335
  explanation: {
249
336
  when: {
250
- expression: definition.expression,
251
337
  events: clauses.map((clause) => ({
252
338
  id: clause.id,
253
339
  event: clause.event,
@@ -255,12 +341,15 @@ export class TriggerPlanner {
255
341
  arguments: clause.arguments,
256
342
  where: clause.where,
257
343
  })),
344
+ pattern: definition.pattern,
258
345
  withinMs: definition.withinMs,
346
+ eventTime: definition.eventTime ?? { allowedLatenessMs: 0 },
259
347
  },
260
348
  then: {
261
- target: definition.target,
349
+ conditionOnly: definition.conditionOnly,
350
+ target: definition.target ?? null,
262
351
  instruction: definition.continuation?.instruction ?? null,
263
- evidence: definition.continuation?.contextPolicy?.evidence ?? 'matched_events',
352
+ evidence: definition.continuation?.contextPolicy?.evidence ?? null,
264
353
  },
265
354
  },
266
355
  };
@@ -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