mcp-event-intelligence 0.1.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 (63) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/LICENSE +201 -0
  3. package/README.md +298 -0
  4. package/SECURITY.md +42 -0
  5. package/bin/mcp-event-intelligence.mjs +52 -0
  6. package/dist/src/composite/engine.d.ts +87 -0
  7. package/dist/src/composite/engine.js +667 -0
  8. package/dist/src/composite/temporal.d.ts +32 -0
  9. package/dist/src/composite/temporal.js +317 -0
  10. package/dist/src/intelligenceProtocol/audit.d.ts +19 -0
  11. package/dist/src/intelligenceProtocol/audit.js +56 -0
  12. package/dist/src/intelligenceProtocol/canonical.d.ts +2 -0
  13. package/dist/src/intelligenceProtocol/canonical.js +21 -0
  14. package/dist/src/intelligenceProtocol/index.d.ts +12 -0
  15. package/dist/src/intelligenceProtocol/index.js +8 -0
  16. package/dist/src/intelligenceProtocol/invariants.d.ts +3 -0
  17. package/dist/src/intelligenceProtocol/invariants.js +36 -0
  18. package/dist/src/intelligenceProtocol/lifecycle.d.ts +4 -0
  19. package/dist/src/intelligenceProtocol/lifecycle.js +24 -0
  20. package/dist/src/intelligenceProtocol/mapper.d.ts +14 -0
  21. package/dist/src/intelligenceProtocol/mapper.js +28 -0
  22. package/dist/src/intelligenceProtocol/schemas.d.ts +325 -0
  23. package/dist/src/intelligenceProtocol/schemas.js +182 -0
  24. package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +488 -0
  25. package/dist/src/intelligenceProtocol/triggerSchemas.js +379 -0
  26. package/dist/src/intelligenceProtocol/version.d.ts +7 -0
  27. package/dist/src/intelligenceProtocol/version.js +7 -0
  28. package/dist/src/mcpEvents/consumer.d.ts +7 -0
  29. package/dist/src/mcpEvents/consumer.js +14 -0
  30. package/dist/src/mcpEvents/server.d.ts +41 -0
  31. package/dist/src/mcpEvents/server.js +122 -0
  32. package/dist/src/protocol/types.d.ts +37 -0
  33. package/dist/src/protocol/types.js +1 -0
  34. package/dist/src/semantic/conditionEngine.d.ts +7 -0
  35. package/dist/src/semantic/conditionEngine.js +65 -0
  36. package/dist/src/semantic/types.d.ts +31 -0
  37. package/dist/src/semantic/types.js +1 -0
  38. package/dist/src/semantic/typesafeJevEvaluator.d.ts +23 -0
  39. package/dist/src/semantic/typesafeJevEvaluator.js +95 -0
  40. package/package.json +85 -0
  41. package/scripts/host-integration.d.mts +86 -0
  42. package/scripts/host-integration.mjs +216 -0
  43. package/scripts/lib/composite-event-consumer.mjs +172 -0
  44. package/scripts/lib/composite-wake-coordinator.mjs +185 -0
  45. package/scripts/lib/derived-contract-registry.mjs +210 -0
  46. package/scripts/lib/derived-event-coordinator.mjs +242 -0
  47. package/scripts/lib/event-processor.mjs +409 -0
  48. package/scripts/lib/generic-runtime-wake.mjs +76 -0
  49. package/scripts/lib/github-mcp-events-adapter.mjs +66 -0
  50. package/scripts/lib/github-webhook.mjs +88 -0
  51. package/scripts/lib/http-wake-deliverer.mjs +31 -0
  52. package/scripts/lib/local-event-intelligence-runtime.mjs +193 -0
  53. package/scripts/lib/mcp-events-client.mjs +519 -0
  54. package/scripts/lib/persistent-event-store.mjs +769 -0
  55. package/scripts/lib/temporal-deadline-scheduler.mjs +111 -0
  56. package/scripts/lib/trigger-control-plane.mjs +939 -0
  57. package/scripts/lib/trigger-inspector.mjs +631 -0
  58. package/scripts/lib/wake-signature.mjs +10 -0
  59. package/scripts/mcp-stdio-server.mjs +380 -0
  60. package/scripts/mcp-stdio.mjs +3 -0
  61. package/scripts/run-tests.mjs +51 -0
  62. package/scripts/service.mjs +756 -0
  63. package/server.json +68 -0
@@ -0,0 +1,317 @@
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
+ const WEEKDAY_TO_ISO = {
25
+ Mon: 1,
26
+ Tue: 2,
27
+ Wed: 3,
28
+ Thu: 4,
29
+ Fri: 5,
30
+ Sat: 6,
31
+ Sun: 7,
32
+ };
33
+ export function zonedParts(timestamp, timeZone) {
34
+ const date = timestamp instanceof Date ? timestamp : new Date(timestamp);
35
+ const formatter = new Intl.DateTimeFormat('en-CA', {
36
+ timeZone,
37
+ year: 'numeric',
38
+ month: '2-digit',
39
+ day: '2-digit',
40
+ hour: '2-digit',
41
+ minute: '2-digit',
42
+ second: '2-digit',
43
+ hourCycle: 'h23',
44
+ weekday: 'short',
45
+ });
46
+ const raw = Object.fromEntries(formatter.formatToParts(date).map((part) => [part.type, part.value]));
47
+ const year = Number(raw.year);
48
+ const month = Number(raw.month);
49
+ const day = Number(raw.day);
50
+ return {
51
+ year,
52
+ month,
53
+ day,
54
+ hour: Number(raw.hour),
55
+ minute: Number(raw.minute),
56
+ second: Number(raw.second),
57
+ weekday: WEEKDAY_TO_ISO[String(raw.weekday)],
58
+ date: `${String(year).padStart(4, '0')}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
59
+ };
60
+ }
61
+ export function localDateTimeToUtc(date, clock, timeZone) {
62
+ const [hour, minute] = clock.split(':').map(Number);
63
+ const desiredAsUtc = Date.UTC(date.year, date.month - 1, date.day, hour, minute, 0, 0);
64
+ let guess = desiredAsUtc;
65
+ for (let attempt = 0; attempt < 4; attempt += 1) {
66
+ const actual = zonedParts(new Date(guess), timeZone);
67
+ const actualAsUtc = Date.UTC(actual.year, actual.month - 1, actual.day, actual.hour, actual.minute, actual.second, 0);
68
+ const delta = desiredAsUtc - actualAsUtc;
69
+ if (delta === 0)
70
+ break;
71
+ guess += delta;
72
+ }
73
+ return new Date(guess);
74
+ }
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
+ }
@@ -0,0 +1,19 @@
1
+ import { type AuditRecord, type LifecycleState } from './schemas.js';
2
+ export interface AppendAuditInput {
3
+ auditId: string;
4
+ traceId: string;
5
+ timestamp: string;
6
+ kind: AuditRecord['kind'];
7
+ entityType: AuditRecord['entityType'];
8
+ entityId: string;
9
+ fromState?: LifecycleState | null;
10
+ toState?: LifecycleState | null;
11
+ details?: Record<string, unknown>;
12
+ }
13
+ export declare class AuditChain {
14
+ private readonly records;
15
+ constructor(initialRecords?: AuditRecord[]);
16
+ append(input: AppendAuditInput): Promise<AuditRecord>;
17
+ list(): AuditRecord[];
18
+ verify(): Promise<boolean>;
19
+ }
@@ -0,0 +1,56 @@
1
+ import { AuditRecordSchema, } from './schemas.js';
2
+ import { canonicalJson, sha256Hex } from './canonical.js';
3
+ import { assertTransition } from './lifecycle.js';
4
+ export class AuditChain {
5
+ records;
6
+ constructor(initialRecords = []) {
7
+ this.records = initialRecords.map((record) => AuditRecordSchema.parse(record));
8
+ }
9
+ async append(input) {
10
+ if (input.fromState && input.toState) {
11
+ assertTransition(input.fromState, input.toState);
12
+ }
13
+ const previousHash = this.records.length === 0
14
+ ? null
15
+ : this.records[this.records.length - 1].hash;
16
+ const unsigned = {
17
+ auditId: input.auditId,
18
+ sequence: this.records.length,
19
+ traceId: input.traceId,
20
+ timestamp: input.timestamp,
21
+ kind: input.kind,
22
+ entityType: input.entityType,
23
+ entityId: input.entityId,
24
+ ...(input.fromState !== undefined ? { fromState: input.fromState } : {}),
25
+ ...(input.toState !== undefined ? { toState: input.toState } : {}),
26
+ details: input.details ?? {},
27
+ previousHash,
28
+ };
29
+ const hash = await sha256Hex(canonicalJson(unsigned));
30
+ const record = AuditRecordSchema.parse({ ...unsigned, hash });
31
+ this.records.push(record);
32
+ return record;
33
+ }
34
+ list() {
35
+ return this.records.map((record) => ({
36
+ ...record,
37
+ details: { ...record.details },
38
+ }));
39
+ }
40
+ async verify() {
41
+ let previousHash = null;
42
+ for (let index = 0; index < this.records.length; index += 1) {
43
+ const record = this.records[index];
44
+ if (record.sequence !== index)
45
+ return false;
46
+ if (record.previousHash !== previousHash)
47
+ return false;
48
+ const { hash, ...unsigned } = record;
49
+ const expected = await sha256Hex(canonicalJson(unsigned));
50
+ if (hash !== expected)
51
+ return false;
52
+ previousHash = hash;
53
+ }
54
+ return true;
55
+ }
56
+ }
@@ -0,0 +1,2 @@
1
+ export declare function canonicalJson(value: unknown): string;
2
+ export declare function sha256Hex(value: unknown): Promise<string>;
@@ -0,0 +1,21 @@
1
+ function normalize(value) {
2
+ if (Array.isArray(value)) {
3
+ return value.map(normalize);
4
+ }
5
+ if (value !== null && typeof value === 'object') {
6
+ return Object.fromEntries(Object.entries(value)
7
+ .sort(([a], [b]) => a.localeCompare(b))
8
+ .map(([key, item]) => [key, normalize(item)]));
9
+ }
10
+ return value;
11
+ }
12
+ export function canonicalJson(value) {
13
+ return JSON.stringify(normalize(value));
14
+ }
15
+ export async function sha256Hex(value) {
16
+ const bytes = new TextEncoder().encode(typeof value === 'string' ? value : canonicalJson(value));
17
+ const digest = await globalThis.crypto.subtle.digest('SHA-256', bytes);
18
+ return [...new Uint8Array(digest)]
19
+ .map((byte) => byte.toString(16).padStart(2, '0'))
20
+ .join('');
21
+ }
@@ -0,0 +1,12 @@
1
+ export { EVENT_INTELLIGENCE_PROTOCOL, EVENT_INTELLIGENCE_PROTOCOL_VERSION, EVENT_INTELLIGENCE_SCHEMA_VERSION, EVENT_INTELLIGENCE_COMPATIBILITY, } from './version.js';
2
+ export { RuntimeTargetSchema, EventSourceSchema, EventLineageSchema, McpEventOccurrenceSchema, SemanticConditionWireSchema, EventIngestRequestSchema, SemanticDecisionRecordSchema, WakeRecordSchema, LifecycleStateSchema, AuditKindSchema, AuditRecordSchema, parseEventIngestRequest, parseEventLineage, parseSemanticDecisionRecord, parseWakeRecord, parseAuditRecord, } from './schemas.js';
3
+ export type { RuntimeTarget, McpEventOccurrence, SemanticConditionWire, EventIngestRequest, EventLineage, SemanticDecisionRecord, WakeRecord, LifecycleState, AuditRecord, } from './schemas.js';
4
+ export { canTransition, assertTransition, allowedTransitions, } from './lifecycle.js';
5
+ export { canonicalJson, sha256Hex, } from './canonical.js';
6
+ export { AuditChain, } from './audit.js';
7
+ export type { AppendAuditInput, } from './audit.js';
8
+ export { mapMcpEventToLineage, } from './mapper.js';
9
+ export type { MapMcpEventInput, } from './mapper.js';
10
+ export { assertDecisionLineage, assertWakeLineage, } from './invariants.js';
11
+ export { COMPOSITE_TRIGGER_PROTOCOL_VERSION, COMPOSITE_TRIGGER_SCHEMA_VERSION, StructuredPredicateSchema, TriggerClauseSchema, DerivedEventProjectionSchema, DerivedEventDefinitionSchema, DerivedEventEvidenceRefSchema, DerivedEventRecordSchema, DeterministicCorrelationSchema, SemanticCorrelationSchema, CompositeTriggerDefinitionSchema, CorrelatableEventSchema, TriggerSourceEventSchema, CompositeCorrelationDecisionSchema, TriggerMatchStatusSchema, TriggerMatchRecordSchema, parseCompositeTriggerDefinition, parseCorrelatableEvent, parseTriggerMatchRecord, parseDerivedEventRecord, } from './triggerSchemas.js';
12
+ export type { StructuredPredicate, TriggerClause, DerivedEventDefinition, DerivedEventRecord, CompositeTriggerDefinition, CorrelatableEvent, TriggerSourceEvent, TriggerMatchRecord, CompositeCorrelationDecision, } from './triggerSchemas.js';
@@ -0,0 +1,8 @@
1
+ export { EVENT_INTELLIGENCE_PROTOCOL, EVENT_INTELLIGENCE_PROTOCOL_VERSION, EVENT_INTELLIGENCE_SCHEMA_VERSION, EVENT_INTELLIGENCE_COMPATIBILITY, } from './version.js';
2
+ export { RuntimeTargetSchema, EventSourceSchema, EventLineageSchema, McpEventOccurrenceSchema, SemanticConditionWireSchema, EventIngestRequestSchema, SemanticDecisionRecordSchema, WakeRecordSchema, LifecycleStateSchema, AuditKindSchema, AuditRecordSchema, parseEventIngestRequest, parseEventLineage, parseSemanticDecisionRecord, parseWakeRecord, parseAuditRecord, } from './schemas.js';
3
+ export { canTransition, assertTransition, allowedTransitions, } from './lifecycle.js';
4
+ export { canonicalJson, sha256Hex, } from './canonical.js';
5
+ export { AuditChain, } from './audit.js';
6
+ export { mapMcpEventToLineage, } from './mapper.js';
7
+ export { assertDecisionLineage, assertWakeLineage, } from './invariants.js';
8
+ export { COMPOSITE_TRIGGER_PROTOCOL_VERSION, COMPOSITE_TRIGGER_SCHEMA_VERSION, StructuredPredicateSchema, TriggerClauseSchema, DerivedEventProjectionSchema, DerivedEventDefinitionSchema, DerivedEventEvidenceRefSchema, DerivedEventRecordSchema, DeterministicCorrelationSchema, SemanticCorrelationSchema, CompositeTriggerDefinitionSchema, CorrelatableEventSchema, TriggerSourceEventSchema, CompositeCorrelationDecisionSchema, TriggerMatchStatusSchema, TriggerMatchRecordSchema, parseCompositeTriggerDefinition, parseCorrelatableEvent, parseTriggerMatchRecord, parseDerivedEventRecord, } from './triggerSchemas.js';
@@ -0,0 +1,3 @@
1
+ import type { EventLineage, SemanticDecisionRecord, WakeRecord } from './schemas.js';
2
+ export declare function assertDecisionLineage(event: EventLineage, decision: SemanticDecisionRecord): void;
3
+ export declare function assertWakeLineage(event: EventLineage, decision: SemanticDecisionRecord | null, wake: WakeRecord): void;
@@ -0,0 +1,36 @@
1
+ export function assertDecisionLineage(event, decision) {
2
+ if (decision.traceId !== event.traceId) {
3
+ throw new Error('Decision traceId does not match source event');
4
+ }
5
+ if (decision.subscriptionId !== event.subscriptionId) {
6
+ throw new Error('Decision subscriptionId does not match source event');
7
+ }
8
+ if (decision.sourceEventId !== event.sourceEventId) {
9
+ throw new Error('Decision sourceEventId does not match source event');
10
+ }
11
+ }
12
+ export function assertWakeLineage(event, decision, wake) {
13
+ if (wake.traceId !== event.traceId) {
14
+ throw new Error('Wake traceId does not match source event');
15
+ }
16
+ if (wake.subscriptionId !== event.subscriptionId) {
17
+ throw new Error('Wake subscriptionId does not match source event');
18
+ }
19
+ if (wake.sourceEventId !== event.sourceEventId) {
20
+ throw new Error('Wake sourceEventId does not match source event');
21
+ }
22
+ if (decision) {
23
+ assertDecisionLineage(event, decision);
24
+ if (wake.decisionId !== decision.decisionId) {
25
+ throw new Error('Wake decisionId does not match semantic decision');
26
+ }
27
+ }
28
+ else if (wake.decisionId !== null) {
29
+ throw new Error('Wake has decisionId but no decision record was supplied');
30
+ }
31
+ if (wake.target.runtime !== event.target.runtime ||
32
+ wake.target.kind !== event.target.kind ||
33
+ wake.target.id !== event.target.id) {
34
+ throw new Error('Wake target does not match source event target');
35
+ }
36
+ }
@@ -0,0 +1,4 @@
1
+ import type { LifecycleState } from './schemas.js';
2
+ export declare function canTransition(from: LifecycleState, to: LifecycleState): boolean;
3
+ export declare function assertTransition(from: LifecycleState, to: LifecycleState): void;
4
+ export declare function allowedTransitions(from: LifecycleState): LifecycleState[];
@@ -0,0 +1,24 @@
1
+ const ALLOWED = {
2
+ received: new Set(['duplicate', 'evaluating', 'matched', 'failed']),
3
+ duplicate: new Set(),
4
+ evaluating: new Set(['matched', 'rejected', 'escalated', 'failed']),
5
+ matched: new Set(['wake_queued', 'failed']),
6
+ rejected: new Set(),
7
+ escalated: new Set(['wake_queued', 'rejected', 'failed']),
8
+ wake_queued: new Set(['wake_delivered', 'dead_letter', 'failed']),
9
+ wake_delivered: new Set(['handled', 'dead_letter', 'failed']),
10
+ handled: new Set(),
11
+ failed: new Set(['dead_letter']),
12
+ dead_letter: new Set(),
13
+ };
14
+ export function canTransition(from, to) {
15
+ return ALLOWED[from].has(to);
16
+ }
17
+ export function assertTransition(from, to) {
18
+ if (!canTransition(from, to)) {
19
+ throw new Error(`Invalid event-intelligence lifecycle transition: ${from} -> ${to}`);
20
+ }
21
+ }
22
+ export function allowedTransitions(from) {
23
+ return [...ALLOWED[from]];
24
+ }
@@ -0,0 +1,14 @@
1
+ import type { EventOccurrence } from '../protocol/types.js';
2
+ import { type EventLineage, type RuntimeTarget } from './schemas.js';
3
+ export interface MapMcpEventInput {
4
+ event: EventOccurrence;
5
+ traceId: string;
6
+ environmentId: string;
7
+ subscriptionId: string;
8
+ serverId: string;
9
+ transport: 'stdio' | 'streamable_http' | 'webhook' | 'poll' | 'internal';
10
+ provider?: string;
11
+ target: RuntimeTarget;
12
+ observedAt: string;
13
+ }
14
+ export declare function mapMcpEventToLineage(input: MapMcpEventInput): Promise<EventLineage>;
@@ -0,0 +1,28 @@
1
+ import { sha256Hex } from './canonical.js';
2
+ import { EventLineageSchema, } from './schemas.js';
3
+ import { EVENT_INTELLIGENCE_PROTOCOL, EVENT_INTELLIGENCE_PROTOCOL_VERSION, EVENT_INTELLIGENCE_SCHEMA_VERSION, } from './version.js';
4
+ export async function mapMcpEventToLineage(input) {
5
+ const payloadHash = await sha256Hex(input.event.data);
6
+ return EventLineageSchema.parse({
7
+ protocol: EVENT_INTELLIGENCE_PROTOCOL,
8
+ protocolVersion: EVENT_INTELLIGENCE_PROTOCOL_VERSION,
9
+ schemaVersion: EVENT_INTELLIGENCE_SCHEMA_VERSION,
10
+ traceId: input.traceId,
11
+ environmentId: input.environmentId,
12
+ subscriptionId: input.subscriptionId,
13
+ sourceEventId: input.event.eventId,
14
+ observedAt: input.observedAt,
15
+ source: {
16
+ serverId: input.serverId,
17
+ transport: input.transport,
18
+ ...(input.provider ? { provider: input.provider } : {}),
19
+ cursor: input.event.cursor ?? null,
20
+ },
21
+ target: input.target,
22
+ event: {
23
+ name: input.event.name,
24
+ occurredAt: input.event.timestamp,
25
+ payloadHash,
26
+ },
27
+ });
28
+ }