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,8 +1,4 @@
1
- import type { CompositeTriggerDefinition, CorrelatableEvent, TriggerClause, TriggerMatchRecord, TriggerSourceEvent } from '../intelligenceProtocol/triggerSchemas.js';
1
+ import type { CorrelatableEvent, TriggerClause, TriggerSourceEvent } from '../intelligenceProtocol/triggerSchemas.js';
2
2
  export declare function clauseMatches(clause: TriggerClause, event: CorrelatableEvent): boolean;
3
3
  export declare function asSourceEvent(clauseId: string, event: CorrelatableEvent): TriggerSourceEvent;
4
- export declare function expressionSatisfied(definition: CompositeTriggerDefinition, record: TriggerMatchRecord): boolean;
5
- export declare function semanticSource(record: TriggerMatchRecord): Record<string, unknown>;
6
- export declare function deterministicKeyForClause(definition: CompositeTriggerDefinition, clauseId: string, event: CorrelatableEvent): string | null;
7
4
  export declare function sameEventIdentity(source: Pick<TriggerSourceEvent, 'sourceEventId' | 'serverId' | 'subscriptionArguments'>, event: Pick<CorrelatableEvent, 'sourceEventId' | 'serverId' | 'subscriptionArguments'>): boolean;
8
- export declare function matchesCorrelationKey(record: TriggerMatchRecord, key: string | null): boolean;
@@ -26,13 +26,6 @@ function sameSubscriptionArguments(left, right) {
26
26
  return JSON.stringify(stableValue(left ?? {})) ===
27
27
  JSON.stringify(stableValue(right ?? {}));
28
28
  }
29
- function scalarKey(value) {
30
- if (typeof value === 'string' ||
31
- typeof value === 'number' ||
32
- typeof value === 'boolean')
33
- return String(value);
34
- return null;
35
- }
36
29
  function predicateMatches(event, predicate) {
37
30
  const value = getByPath(event.data, predicate.path);
38
31
  switch (predicate.op) {
@@ -46,10 +39,54 @@ function predicateMatches(event, predicate) {
46
39
  if (Array.isArray(value))
47
40
  return value.includes(predicate.value);
48
41
  return false;
42
+ case 'startsWith':
43
+ return typeof value === 'string' && value.startsWith(predicate.value);
44
+ case 'endsWith':
45
+ return typeof value === 'string' && value.endsWith(predicate.value);
46
+ case 'regex':
47
+ if (typeof value !== 'string')
48
+ return false;
49
+ try {
50
+ return new RegExp(predicate.value, predicate.flags ?? '').test(value);
51
+ }
52
+ catch {
53
+ return false;
54
+ }
49
55
  case 'in':
50
56
  return predicate.value.includes(value);
57
+ case 'notIn':
58
+ return !predicate.value.includes(value);
59
+ case 'between': {
60
+ const [lower, upper] = predicate.value;
61
+ if (typeof value === 'number') {
62
+ return (typeof lower === 'number' &&
63
+ typeof upper === 'number' &&
64
+ value >= lower &&
65
+ value <= upper);
66
+ }
67
+ if (typeof value === 'string') {
68
+ return (typeof lower === 'string' &&
69
+ typeof upper === 'string' &&
70
+ value >= lower &&
71
+ value <= upper);
72
+ }
73
+ return false;
74
+ }
51
75
  case 'exists':
52
76
  return predicate.value ? value !== undefined : value === undefined;
77
+ case 'isNull':
78
+ return predicate.value
79
+ ? value === null || value === undefined
80
+ : value !== null && value !== undefined;
81
+ case 'type':
82
+ if (predicate.value === 'null')
83
+ return value === null;
84
+ if (predicate.value === 'array')
85
+ return Array.isArray(value);
86
+ if (predicate.value === 'object') {
87
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
88
+ }
89
+ return typeof value === predicate.value;
53
90
  case 'gt':
54
91
  return typeof value === 'number' && value > predicate.value;
55
92
  case 'gte':
@@ -76,6 +113,7 @@ export function asSourceEvent(clauseId, event) {
76
113
  sourceEventId: event.sourceEventId,
77
114
  eventName: event.name,
78
115
  occurredAt: event.occurredAt,
116
+ ...(event.receivedAt ? { receivedAt: event.receivedAt } : {}),
79
117
  ...(event.provider ? { provider: event.provider } : {}),
80
118
  ...(event.serverId ? { serverId: event.serverId } : {}),
81
119
  subscriptionArguments: event.subscriptionArguments ?? {},
@@ -83,55 +121,8 @@ export function asSourceEvent(clauseId, event) {
83
121
  data: event.data,
84
122
  };
85
123
  }
86
- function clauseEvents(record, ref) {
87
- return record.sourceEvents
88
- .filter((event) => event.clauseId === ref)
89
- .sort((a, b) => Date.parse(a.occurredAt) - Date.parse(b.occurredAt));
90
- }
91
- export function expressionSatisfied(definition, record) {
92
- const expression = definition.expression;
93
- if (expression.kind === 'allOf') {
94
- return expression.refs.every((ref) => clauseEvents(record, ref).length > 0);
95
- }
96
- if (expression.kind === 'anyOf') {
97
- return expression.refs.some((ref) => clauseEvents(record, ref).length > 0);
98
- }
99
- if (expression.kind === 'count') {
100
- return clauseEvents(record, expression.ref).length >= expression.atLeast;
101
- }
102
- let previous = Number.NEGATIVE_INFINITY;
103
- for (const ref of expression.refs) {
104
- const candidate = clauseEvents(record, ref)
105
- .find((event) => Date.parse(event.occurredAt) > previous);
106
- if (!candidate)
107
- return false;
108
- previous = Date.parse(candidate.occurredAt);
109
- }
110
- return true;
111
- }
112
- export function semanticSource(record) {
113
- const source = {};
114
- for (const event of record.sourceEvents) {
115
- source[event.clauseId] = event.data;
116
- }
117
- return source;
118
- }
119
- export function deterministicKeyForClause(definition, clauseId, event) {
120
- const deterministic = definition.correlation?.deterministic;
121
- if (!deterministic)
122
- return null;
123
- const field = deterministic.fields.find((item) => item.ref === clauseId);
124
- if (!field)
125
- return null;
126
- return scalarKey(getByPath(event.data, field.path));
127
- }
128
124
  export function sameEventIdentity(source, event) {
129
125
  return (source.sourceEventId === event.sourceEventId &&
130
126
  (source.serverId ?? null) === (event.serverId ?? null) &&
131
127
  sameSubscriptionArguments(source.subscriptionArguments, event.subscriptionArguments));
132
128
  }
133
- export function matchesCorrelationKey(record, key) {
134
- if (record.correlationKey === null || key === null)
135
- return true;
136
- return record.correlationKey === key;
137
- }
@@ -17,7 +17,9 @@ export class CompositeTriggerRuntimeSupport {
17
17
  deadline.conditionId,
18
18
  deadline.dueAt,
19
19
  ].join(':'))).slice(0, 24)}`;
20
- const existing = this.store.getTemporalDeadline?.(deadlineId);
20
+ const existing = this.store.getTemporalDeadline
21
+ ? await this.store.getTemporalDeadline(deadlineId)
22
+ : null;
21
23
  if (existing?.status === 'pending' || existing?.status === 'fired') {
22
24
  continue;
23
25
  }
@@ -36,7 +38,7 @@ export class CompositeTriggerRuntimeSupport {
36
38
  if (!this.store.listTemporalDeadlines ||
37
39
  !this.store.setTemporalDeadlineStatus)
38
40
  return;
39
- const pending = this.store.listTemporalDeadlines({
41
+ const pending = await this.store.listTemporalDeadlines({
40
42
  matchId,
41
43
  status: 'pending',
42
44
  });
@@ -45,9 +47,11 @@ export class CompositeTriggerRuntimeSupport {
45
47
  }
46
48
  }
47
49
  async advanceLifecycleAfterEffect(match, effectKind, effectId) {
48
- const definition = this.store.listTriggers().find((candidate) => candidate.triggerId === match.triggerId &&
50
+ const definition = (await this.store.listTriggers()).find((candidate) => candidate.triggerId === match.triggerId &&
49
51
  candidate.version === match.triggerVersion);
50
- const triggerState = this.store.getTriggerState?.(match.triggerId, match.triggerVersion) ?? null;
52
+ const triggerState = this.store.getTriggerState
53
+ ? await this.store.getTriggerState(match.triggerId, match.triggerVersion)
54
+ : null;
51
55
  if (!definition || !this.store.setTriggerState)
52
56
  return;
53
57
  const previousCount = triggerState?.fireCount ?? 0;
@@ -158,7 +162,7 @@ export class CompositeTriggerRuntimeSupport {
158
162
  }
159
163
  async auditMatch(record, kind, details = {}) {
160
164
  await this.store.appendAudit({
161
- auditId: `audit_${(await sha256Hex(`${record.matchId}:${kind}:${record.updatedAt}:${this.store.listTriggerMatches().length}`)).slice(0, 24)}`,
165
+ auditId: `audit_${(await sha256Hex(`${record.matchId}:${kind}:${record.updatedAt}:${(await this.store.listTriggerMatches()).length}`)).slice(0, 24)}`,
162
166
  traceId: record.sourceEvents[0]?.traceId ?? record.matchId,
163
167
  timestamp: this.now().toISOString(),
164
168
  kind,
@@ -168,7 +172,7 @@ export class CompositeTriggerRuntimeSupport {
168
172
  triggerId: record.triggerId,
169
173
  triggerVersion: record.triggerVersion,
170
174
  status: record.status,
171
- correlationKey: record.correlationKey,
175
+ partitionKey: record.partitionKey,
172
176
  ...details,
173
177
  },
174
178
  });
@@ -1,7 +1,25 @@
1
1
  import type { CompositeTriggerDefinition, TriggerMatchRecord } from '../intelligenceProtocol/triggerSchemas.js';
2
2
  export interface CompositeTriggerStore {
3
+ storeCapabilities?(): {
4
+ partitionLeases?: string;
5
+ [key: string]: unknown;
6
+ };
7
+ claimPartitionLease?(partitionKey: string, options: {
8
+ workerId: string;
9
+ now?: string;
10
+ leaseMs?: number;
11
+ }): Promise<unknown | null>;
12
+ renewPartitionLease?(partitionKey: string, options: {
13
+ workerId: string;
14
+ now?: string;
15
+ leaseMs?: number;
16
+ }): Promise<unknown | null>;
17
+ releasePartitionLease?(partitionKey: string, options: {
18
+ workerId: string;
19
+ now?: string;
20
+ }): Promise<boolean>;
3
21
  putTrigger(definition: CompositeTriggerDefinition): Promise<void>;
4
- listTriggers(): CompositeTriggerDefinition[];
22
+ listTriggers(): CompositeTriggerDefinition[] | Promise<CompositeTriggerDefinition[]>;
5
23
  getTriggerState?(triggerId: string, version: string): {
6
24
  status: 'active' | 'paused' | 'completed' | 'expired' | 'deleted';
7
25
  owner?: unknown;
@@ -12,7 +30,7 @@ export interface CompositeTriggerStore {
12
30
  } | null;
13
31
  setTriggerState?(triggerId: string, version: string, status: 'active' | 'paused' | 'completed' | 'expired' | 'deleted', actor?: unknown, metadata?: Record<string, unknown>): Promise<unknown>;
14
32
  appendTriggerMatch(record: TriggerMatchRecord): Promise<void>;
15
- listTriggerMatches(triggerId?: string): TriggerMatchRecord[];
33
+ listTriggerMatches(triggerId?: string): TriggerMatchRecord[] | Promise<TriggerMatchRecord[]>;
16
34
  getTemporalDeadline?(deadlineId: string): {
17
35
  deadlineId: string;
18
36
  triggerId: string;
@@ -21,7 +39,15 @@ export interface CompositeTriggerStore {
21
39
  conditionId: string;
22
40
  dueAt: string;
23
41
  status: 'pending' | 'cancelled' | 'fired';
24
- } | null;
42
+ } | null | Promise<{
43
+ deadlineId: string;
44
+ triggerId: string;
45
+ triggerVersion: string;
46
+ matchId: string;
47
+ conditionId: string;
48
+ dueAt: string;
49
+ status: 'pending' | 'cancelled' | 'fired';
50
+ } | null>;
25
51
  listTemporalDeadlines?(filter?: {
26
52
  triggerId?: string;
27
53
  matchId?: string;
@@ -34,7 +60,15 @@ export interface CompositeTriggerStore {
34
60
  conditionId: string;
35
61
  dueAt: string;
36
62
  status: string;
37
- }>;
63
+ }> | Promise<Array<{
64
+ deadlineId: string;
65
+ triggerId: string;
66
+ triggerVersion: string;
67
+ matchId: string;
68
+ conditionId: string;
69
+ dueAt: string;
70
+ status: string;
71
+ }>>;
38
72
  putTemporalDeadline?(input: {
39
73
  deadlineId: string;
40
74
  triggerId: string;
@@ -1,19 +1,3 @@
1
- import type { CompositeTriggerDefinition, TemporalCondition, TriggerMatchRecord } from '../intelligenceProtocol/triggerSchemas.js';
2
- export interface TemporalPendingDeadline {
3
- conditionId: string;
4
- dueAt: string;
5
- }
6
- export interface TemporalEvaluation {
7
- outcome: 'satisfied' | 'pending' | 'blocked';
8
- pendingDeadlines: TemporalPendingDeadline[];
9
- conditionStates: Array<{
10
- conditionId: string;
11
- kind: TemporalCondition['kind'];
12
- status: 'satisfied' | 'pending' | 'blocked';
13
- dueAt?: string;
14
- reason?: string;
15
- }>;
16
- }
17
1
  export declare function zonedParts(timestamp: string | Date, timeZone: string): {
18
2
  year: number;
19
3
  month: number;
@@ -29,4 +13,3 @@ export declare function localDateTimeToUtc(date: {
29
13
  month: number;
30
14
  day: number;
31
15
  }, clock: string, timeZone: string): Date;
32
- export declare function evaluateTemporalConditions(definition: CompositeTriggerDefinition, record: TriggerMatchRecord, now: Date): TemporalEvaluation;
@@ -1,26 +1,3 @@
1
- function getByPath(source, path) {
2
- let current = source;
3
- for (const part of path.split('.')) {
4
- if (current === null ||
5
- typeof current !== 'object' ||
6
- Array.isArray(current))
7
- return undefined;
8
- current = current[part];
9
- }
10
- return current;
11
- }
12
- function eventsFor(record, ref) {
13
- return record.sourceEvents
14
- .filter((event) => event.clauseId === ref)
15
- .sort((a, b) => Date.parse(a.occurredAt) - Date.parse(b.occurredAt));
16
- }
17
- function latestEvent(record, ref) {
18
- return eventsFor(record, ref).at(-1) ?? null;
19
- }
20
- function parseClock(value) {
21
- const [hour, minute] = value.split(':').map(Number);
22
- return hour * 60 + minute;
23
- }
24
1
  const WEEKDAY_TO_ISO = {
25
2
  Mon: 1,
26
3
  Tue: 2,
@@ -72,246 +49,3 @@ export function localDateTimeToUtc(date, clock, timeZone) {
72
49
  }
73
50
  return new Date(guess);
74
51
  }
75
- function calendarMatches(condition, event) {
76
- const parts = zonedParts(event.occurredAt, condition.timezone);
77
- const minutes = parts.hour * 60 + parts.minute;
78
- if (condition.weekdays && !condition.weekdays.includes(parts.weekday)) {
79
- return false;
80
- }
81
- if (condition.dates && !condition.dates.includes(parts.date)) {
82
- return false;
83
- }
84
- if (condition.dateRange &&
85
- (parts.date < condition.dateRange.start ||
86
- parts.date > condition.dateRange.end)) {
87
- return false;
88
- }
89
- if (condition.dayOfMonth &&
90
- !condition.dayOfMonth.includes(parts.day)) {
91
- return false;
92
- }
93
- if (condition.before && condition.after) {
94
- const before = parseClock(condition.before);
95
- const after = parseClock(condition.after);
96
- if (after <= before) {
97
- if (minutes < after || minutes > before)
98
- return false;
99
- }
100
- else {
101
- if (!(minutes >= after || minutes <= before))
102
- return false;
103
- }
104
- }
105
- else {
106
- if (condition.before && minutes > parseClock(condition.before)) {
107
- return false;
108
- }
109
- if (condition.after && minutes < parseClock(condition.after)) {
110
- return false;
111
- }
112
- }
113
- return true;
114
- }
115
- function absenceDueAt(condition, anchor) {
116
- const anchorMs = Date.parse(anchor.occurredAt);
117
- if (condition.forMs) {
118
- return new Date(anchorMs + condition.forMs);
119
- }
120
- const zone = condition.timezone;
121
- const parts = zonedParts(anchor.occurredAt, zone);
122
- const due = localDateTimeToUtc(parts, condition.untilLocalTime, zone);
123
- return due.getTime() < anchorMs ? new Date(anchorMs) : due;
124
- }
125
- function state(condition, status, extra = {}) {
126
- return {
127
- conditionId: condition.id,
128
- kind: condition.kind,
129
- status,
130
- ...extra,
131
- };
132
- }
133
- export function evaluateTemporalConditions(definition, record, now) {
134
- const states = [];
135
- const pendingDeadlines = [];
136
- for (const condition of definition.temporal ?? []) {
137
- if (condition.kind === 'calendar') {
138
- const event = latestEvent(record, condition.ref);
139
- if (!event) {
140
- states.push(state(condition, 'pending', {
141
- reason: 'referenced event has not occurred',
142
- }));
143
- continue;
144
- }
145
- states.push(calendarMatches(condition, event)
146
- ? state(condition, 'satisfied')
147
- : state(condition, 'blocked', {
148
- reason: 'event is outside the configured calendar window',
149
- }));
150
- continue;
151
- }
152
- if (condition.kind === 'not' || condition.kind === 'unless') {
153
- const exists = eventsFor(record, condition.ref).length > 0;
154
- states.push(exists
155
- ? state(condition, 'blocked', {
156
- reason: `${condition.ref} occurred`,
157
- })
158
- : state(condition, 'satisfied'));
159
- continue;
160
- }
161
- if (condition.kind === 'after') {
162
- const event = latestEvent(record, condition.ref);
163
- const anchor = latestEvent(record, condition.afterRef);
164
- if (!event || !anchor) {
165
- states.push(state(condition, 'pending', {
166
- reason: 'ordering events are incomplete',
167
- }));
168
- }
169
- else if (Date.parse(event.occurredAt) > Date.parse(anchor.occurredAt)) {
170
- states.push(state(condition, 'satisfied'));
171
- }
172
- else {
173
- states.push(state(condition, 'blocked', {
174
- reason: `${condition.ref} did not occur after ${condition.afterRef}`,
175
- }));
176
- }
177
- continue;
178
- }
179
- if (condition.kind === 'until') {
180
- const event = latestEvent(record, condition.ref);
181
- const boundary = latestEvent(record, condition.beforeRef);
182
- if (!event || !boundary) {
183
- states.push(state(condition, 'pending', {
184
- reason: 'ordering events are incomplete',
185
- }));
186
- }
187
- else if (Date.parse(event.occurredAt) <= Date.parse(boundary.occurredAt)) {
188
- states.push(state(condition, 'satisfied'));
189
- }
190
- else {
191
- states.push(state(condition, 'blocked', {
192
- reason: `${condition.ref} occurred after ${condition.beforeRef}`,
193
- }));
194
- }
195
- continue;
196
- }
197
- if (condition.kind === 'threshold') {
198
- const count = eventsFor(record, condition.ref).length;
199
- states.push(count >= condition.atLeast
200
- ? state(condition, 'satisfied')
201
- : state(condition, 'pending', {
202
- reason: `${count}/${condition.atLeast} occurrences`,
203
- }));
204
- continue;
205
- }
206
- if (condition.kind === 'distinct') {
207
- const values = new Set(eventsFor(record, condition.ref)
208
- .map((event) => getByPath(event.data, condition.path))
209
- .filter((value) => typeof value === 'string' ||
210
- typeof value === 'number' ||
211
- typeof value === 'boolean')
212
- .map(String));
213
- states.push(values.size >= condition.atLeast
214
- ? state(condition, 'satisfied')
215
- : state(condition, 'pending', {
216
- reason: `${values.size}/${condition.atLeast} distinct values`,
217
- }));
218
- continue;
219
- }
220
- if (condition.kind === 'rate') {
221
- const events = eventsFor(record, condition.ref);
222
- if (!events.length) {
223
- states.push(state(condition, 'pending', {
224
- reason: 'no events in rate window',
225
- }));
226
- continue;
227
- }
228
- const end = Math.max(now.getTime(), Date.parse(events.at(-1).occurredAt));
229
- const count = events.filter((event) => end - Date.parse(event.occurredAt) <= condition.perMs).length;
230
- states.push(count >= condition.atLeast
231
- ? state(condition, 'satisfied')
232
- : state(condition, 'pending', {
233
- reason: `${count}/${condition.atLeast} occurrences in rate window`,
234
- }));
235
- continue;
236
- }
237
- if (condition.kind === 'debounce') {
238
- const event = latestEvent(record, condition.ref);
239
- if (!event) {
240
- states.push(state(condition, 'pending', {
241
- reason: 'debounced event has not occurred',
242
- }));
243
- continue;
244
- }
245
- const dueAt = new Date(Date.parse(event.occurredAt) + condition.forMs).toISOString();
246
- if (now.getTime() >= Date.parse(dueAt)) {
247
- states.push(state(condition, 'satisfied', { dueAt }));
248
- }
249
- else {
250
- states.push(state(condition, 'pending', {
251
- dueAt,
252
- reason: 'waiting for quiet period',
253
- }));
254
- pendingDeadlines.push({
255
- conditionId: condition.id,
256
- dueAt,
257
- });
258
- }
259
- continue;
260
- }
261
- if (condition.kind === 'absence') {
262
- const anchor = latestEvent(record, condition.afterRef);
263
- if (!anchor) {
264
- states.push(state(condition, 'pending', {
265
- reason: `waiting for anchor ${condition.afterRef}`,
266
- }));
267
- continue;
268
- }
269
- const due = absenceDueAt(condition, anchor);
270
- const dueAt = due.toISOString();
271
- const anchorMs = Date.parse(anchor.occurredAt);
272
- const disqualifying = eventsFor(record, condition.ref)
273
- .some((event) => {
274
- const at = Date.parse(event.occurredAt);
275
- return at >= anchorMs && at <= due.getTime();
276
- });
277
- if (disqualifying) {
278
- states.push(state(condition, 'blocked', {
279
- dueAt,
280
- reason: `${condition.ref} occurred before the absence deadline`,
281
- }));
282
- }
283
- else if (now.getTime() >= due.getTime()) {
284
- states.push(state(condition, 'satisfied', { dueAt }));
285
- }
286
- else {
287
- states.push(state(condition, 'pending', {
288
- dueAt,
289
- reason: 'absence window is still open',
290
- }));
291
- pendingDeadlines.push({
292
- conditionId: condition.id,
293
- dueAt,
294
- });
295
- }
296
- }
297
- }
298
- if (states.some((entry) => entry.status === 'blocked')) {
299
- return {
300
- outcome: 'blocked',
301
- pendingDeadlines,
302
- conditionStates: states,
303
- };
304
- }
305
- if (states.some((entry) => entry.status === 'pending')) {
306
- return {
307
- outcome: 'pending',
308
- pendingDeadlines,
309
- conditionStates: states,
310
- };
311
- }
312
- return {
313
- outcome: 'satisfied',
314
- pendingDeadlines,
315
- conditionStates: states,
316
- };
317
- }
@@ -6,11 +6,12 @@ export declare const ActivationEvidenceSchema: z.ZodObject<{
6
6
  eventName: z.ZodString;
7
7
  traceId: z.ZodString;
8
8
  occurredAt: z.ZodString;
9
+ receivedAt: z.ZodOptional<z.ZodString>;
9
10
  payloadHash: z.ZodNullable<z.ZodString>;
10
11
  data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
11
12
  }, z.core.$strict>;
12
13
  export declare const ActivationEnvelopeSchema: z.ZodObject<{
13
- activationVersion: z.ZodLiteral<"1">;
14
+ activationVersion: z.ZodLiteral<"2">;
14
15
  wake: z.ZodObject<{
15
16
  wakeId: z.ZodString;
16
17
  status: z.ZodString;
@@ -26,7 +27,40 @@ export declare const ActivationEnvelopeSchema: z.ZodObject<{
26
27
  triggerId: z.ZodString;
27
28
  version: z.ZodString;
28
29
  description: z.ZodNullable<z.ZodString>;
29
- expression: z.ZodRecord<z.ZodString, z.ZodUnknown>;
30
+ pattern: z.ZodObject<{
31
+ version: z.ZodDefault<z.ZodLiteral<"2">>;
32
+ root: z.ZodType<any, unknown, z.core.$ZodTypeInternals<any, unknown>>;
33
+ partitionBy: z.ZodDefault<z.ZodArray<z.ZodObject<{
34
+ key: z.ZodString;
35
+ fields: z.ZodArray<z.ZodObject<{
36
+ ref: z.ZodString;
37
+ path: z.ZodString;
38
+ }, z.core.$strict>>;
39
+ }, z.core.$strict>>>;
40
+ selection: z.ZodDefault<z.ZodObject<{
41
+ overlap: z.ZodDefault<z.ZodEnum<{
42
+ allow: "allow";
43
+ disallow: "disallow";
44
+ }>>;
45
+ afterMatch: z.ZodDefault<z.ZodUnion<readonly [z.ZodEnum<{
46
+ skipPastLast: "skipPastLast";
47
+ skipToNext: "skipToNext";
48
+ keepAll: "keepAll";
49
+ }>, z.ZodObject<{
50
+ kind: z.ZodEnum<{
51
+ skipToFirst: "skipToFirst";
52
+ skipToLast: "skipToLast";
53
+ }>;
54
+ ref: z.ZodString;
55
+ }, z.core.$strict>]>>;
56
+ maxMatchesPerEvent: z.ZodDefault<z.ZodNumber>;
57
+ }, z.core.$strict>>;
58
+ execution: z.ZodDefault<z.ZodObject<{
59
+ maxCandidates: z.ZodDefault<z.ZodNumber>;
60
+ maxSemanticEvaluations: z.ZodDefault<z.ZodNumber>;
61
+ maxBufferedEvents: z.ZodDefault<z.ZodNumber>;
62
+ }, z.core.$strict>>;
63
+ }, z.core.$strict>;
30
64
  lifecycle: z.ZodRecord<z.ZodString, z.ZodUnknown>;
31
65
  }, z.core.$strict>;
32
66
  continuation: z.ZodNullable<z.ZodObject<{
@@ -43,7 +77,7 @@ export declare const ActivationEnvelopeSchema: z.ZodObject<{
43
77
  match: z.ZodObject<{
44
78
  matchId: z.ZodString;
45
79
  status: z.ZodString;
46
- correlationKey: z.ZodNullable<z.ZodString>;
80
+ partitionKey: z.ZodNullable<z.ZodString>;
47
81
  openedAt: z.ZodString;
48
82
  updatedAt: z.ZodString;
49
83
  }, z.core.$strict>;
@@ -54,6 +88,7 @@ export declare const ActivationEnvelopeSchema: z.ZodObject<{
54
88
  eventName: z.ZodString;
55
89
  traceId: z.ZodString;
56
90
  occurredAt: z.ZodString;
91
+ receivedAt: z.ZodOptional<z.ZodString>;
57
92
  payloadHash: z.ZodNullable<z.ZodString>;
58
93
  data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
59
94
  }, z.core.$strict>>;
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { ContinuationContractSchema } from './triggerSchemas.js';
3
+ import { PatternAstV2DefinitionSchema } from './patternV2Schemas.js';
3
4
  const Id = z.string().min(1).max(200);
4
5
  const Timestamp = z.string().datetime({ offset: true });
5
6
  export const ActivationEvidenceSchema = z.object({
@@ -9,11 +10,12 @@ export const ActivationEvidenceSchema = z.object({
9
10
  eventName: z.string().min(1),
10
11
  traceId: Id,
11
12
  occurredAt: Timestamp,
13
+ receivedAt: Timestamp.optional(),
12
14
  payloadHash: z.string().regex(/^[a-f0-9]{64}$/).nullable(),
13
15
  data: z.record(z.string(), z.unknown()).optional(),
14
16
  }).strict();
15
17
  export const ActivationEnvelopeSchema = z.object({
16
- activationVersion: z.literal('1'),
18
+ activationVersion: z.literal('2'),
17
19
  wake: z.object({
18
20
  wakeId: Id,
19
21
  status: z.string().min(1),
@@ -29,14 +31,14 @@ export const ActivationEnvelopeSchema = z.object({
29
31
  triggerId: Id,
30
32
  version: z.string().min(1),
31
33
  description: z.string().nullable(),
32
- expression: z.record(z.string(), z.unknown()),
34
+ pattern: PatternAstV2DefinitionSchema,
33
35
  lifecycle: z.record(z.string(), z.unknown()),
34
36
  }).strict(),
35
37
  continuation: ContinuationContractSchema.nullable(),
36
38
  match: z.object({
37
39
  matchId: Id,
38
40
  status: z.string().min(1),
39
- correlationKey: z.string().nullable(),
41
+ partitionKey: z.string().nullable(),
40
42
  openedAt: Timestamp,
41
43
  updatedAt: Timestamp,
42
44
  }).strict(),