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.
- package/CHANGELOG.md +100 -0
- package/README.md +107 -26
- package/SECURITY.md +4 -4
- package/bin/mcp-event-intelligence.mjs +3 -3
- package/dist/src/composite/engine.d.ts +14 -4
- package/dist/src/composite/engine.js +533 -208
- package/dist/src/composite/matching.d.ts +1 -5
- package/dist/src/composite/matching.js +45 -54
- package/dist/src/composite/runtimeSupport.js +10 -6
- package/dist/src/composite/store.d.ts +38 -4
- package/dist/src/composite/temporal.d.ts +0 -17
- package/dist/src/composite/temporal.js +0 -266
- package/dist/src/intelligenceProtocol/activationSchemas.d.ts +38 -3
- package/dist/src/intelligenceProtocol/activationSchemas.js +5 -3
- package/dist/src/intelligenceProtocol/index.d.ts +12 -9
- package/dist/src/intelligenceProtocol/index.js +6 -5
- package/dist/src/intelligenceProtocol/lifecycle.js +2 -10
- package/dist/src/intelligenceProtocol/operatorRegistry.d.ts +300 -0
- package/dist/src/intelligenceProtocol/operatorRegistry.js +338 -0
- package/dist/src/intelligenceProtocol/patternV2Schemas.d.ts +188 -0
- package/dist/src/intelligenceProtocol/patternV2Schemas.js +801 -0
- package/dist/src/intelligenceProtocol/schemas.d.ts +4 -189
- package/dist/src/intelligenceProtocol/schemas.js +3 -97
- package/dist/src/intelligenceProtocol/triggerPlanSchemas.d.ts +114 -38
- package/dist/src/intelligenceProtocol/triggerPlanSchemas.js +27 -18
- package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +268 -213
- package/dist/src/intelligenceProtocol/triggerSchemas.js +155 -169
- package/dist/src/intelligenceProtocol/version.d.ts +2 -2
- package/dist/src/intelligenceProtocol/version.js +2 -2
- package/dist/src/mcpEvents/consumer.d.ts +1 -0
- package/dist/src/mcpEvents/consumer.js +1 -0
- package/dist/src/patternV2/evaluator.d.ts +51 -0
- package/dist/src/patternV2/evaluator.js +986 -0
- package/dist/src/semantic/types.d.ts +6 -0
- package/dist/src/semantic/typesafeJevEvaluator.d.ts +1 -0
- package/dist/src/semantic/typesafeJevEvaluator.js +6 -0
- package/package.json +9 -3
- package/scripts/host-integration.d.mts +54 -4
- package/scripts/host-integration.mjs +25 -4
- package/scripts/lib/activation-hydrator.mjs +13 -12
- package/scripts/lib/composite-event-consumer.mjs +10 -28
- package/scripts/lib/composite-wake-coordinator.mjs +19 -19
- package/scripts/lib/derived-contract-registry.mjs +4 -4
- package/scripts/lib/derived-event-coordinator.mjs +69 -6
- package/scripts/lib/local-event-intelligence-runtime.mjs +35 -3
- package/scripts/lib/mcp-events-client.mjs +97 -55
- package/scripts/lib/mcp-events-compatibility.mjs +54 -0
- package/scripts/lib/persistent-event-store.mjs +228 -61
- package/scripts/lib/store-capabilities.mjs +88 -0
- package/scripts/lib/store-conformance.mjs +127 -0
- package/scripts/lib/temporal-deadline-scheduler.mjs +3 -3
- package/scripts/lib/trigger-control-plane.mjs +202 -53
- package/scripts/lib/trigger-inspector.mjs +50 -147
- package/scripts/lib/trigger-planner.mjs +141 -52
- package/scripts/lib/wake-retry-scheduler.mjs +2 -19
- package/scripts/mcp-stdio-server.mjs +46 -61
- package/scripts/service.mjs +19 -138
- package/server.json +4 -4
- package/dist/src/intelligenceProtocol/invariants.d.ts +0 -3
- package/dist/src/intelligenceProtocol/invariants.js +0 -36
- package/dist/src/intelligenceProtocol/mapper.d.ts +0 -14
- package/dist/src/intelligenceProtocol/mapper.js +0 -28
- package/scripts/lib/event-processor.mjs +0 -624
- package/scripts/lib/http-wake-deliverer.mjs +0 -31
|
@@ -17,6 +17,12 @@ export interface SemanticEvaluation {
|
|
|
17
17
|
metadata?: Record<string, unknown>;
|
|
18
18
|
}
|
|
19
19
|
export interface SemanticEvaluator {
|
|
20
|
+
/**
|
|
21
|
+
* Stable identity for durable semantic-decision caching. It must change when
|
|
22
|
+
* evaluator semantics/model configuration changes. Evaluators that omit it
|
|
23
|
+
* remain eligible only for evaluation-pass caching.
|
|
24
|
+
*/
|
|
25
|
+
readonly cacheIdentity?: string;
|
|
20
26
|
evaluate(request: SemanticEvaluationRequest): Promise<SemanticEvaluation>;
|
|
21
27
|
}
|
|
22
28
|
export type SemanticOutcome = 'match' | 'reject' | 'uncertain';
|
|
@@ -10,6 +10,7 @@ export interface TypeSafeJevEvaluatorOptions {
|
|
|
10
10
|
}
|
|
11
11
|
export declare class TypeSafeJevEvaluator implements SemanticEvaluator {
|
|
12
12
|
private readonly options;
|
|
13
|
+
readonly cacheIdentity: string;
|
|
13
14
|
private readonly model;
|
|
14
15
|
private readonly baseUrl;
|
|
15
16
|
private readonly fetchFn;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export class TypeSafeJevEvaluator {
|
|
2
2
|
options;
|
|
3
|
+
cacheIdentity;
|
|
3
4
|
model;
|
|
4
5
|
baseUrl;
|
|
5
6
|
fetchFn;
|
|
@@ -15,6 +16,11 @@ export class TypeSafeJevEvaluator {
|
|
|
15
16
|
this.fetchFn = options.fetchFn ?? fetch;
|
|
16
17
|
this.maxRetries = options.maxRetries ?? 2;
|
|
17
18
|
this.retryBaseMs = options.retryBaseMs ?? 100;
|
|
19
|
+
this.cacheIdentity = [
|
|
20
|
+
'typesafe-jev',
|
|
21
|
+
this.baseUrl,
|
|
22
|
+
this.model,
|
|
23
|
+
].join(':');
|
|
18
24
|
}
|
|
19
25
|
static fromEnvironment(env) {
|
|
20
26
|
const apiKey = env.TYPESAFE_API_KEY;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-event-intelligence",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "MCP-native complex event processing for durable agent workflows: event-time patterns, semantic predicates, derived events, and targeted wakeups.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/src/intelligenceProtocol/index.js",
|
|
@@ -54,13 +54,19 @@
|
|
|
54
54
|
"mcp": "npm run build && node scripts/mcp-stdio.mjs",
|
|
55
55
|
"mcp:inspect": "npm run build && npx @modelcontextprotocol/inspector node scripts/mcp-stdio.mjs",
|
|
56
56
|
"registry:validate": "mcp-publisher validate server.json",
|
|
57
|
-
"pack:mcp-smoke": "node scripts/package-mcp-smoke.mjs"
|
|
57
|
+
"pack:mcp-smoke": "node scripts/package-mcp-smoke.mjs",
|
|
58
|
+
"conformance:pattern-v2": "npm run build && node scripts/evidence/pattern-v2-conformance.mjs",
|
|
59
|
+
"bench:pattern-v2": "npm run build && node scripts/evidence/pattern-v2-benchmark.mjs --profile quick",
|
|
60
|
+
"evidence:pattern-v2": "npm run build && node scripts/evidence/pattern-v2-conformance.mjs && node scripts/evidence/pattern-v2-benchmark.mjs --profile quick"
|
|
58
61
|
},
|
|
59
62
|
"keywords": [
|
|
60
63
|
"mcp",
|
|
61
64
|
"model-context-protocol",
|
|
62
65
|
"ai-agents",
|
|
63
66
|
"event-driven",
|
|
67
|
+
"complex-event-processing",
|
|
68
|
+
"cep",
|
|
69
|
+
"event-time",
|
|
64
70
|
"temporal",
|
|
65
71
|
"durable-execution",
|
|
66
72
|
"triggers",
|
|
@@ -56,6 +56,8 @@ export interface HostMcpConnectionOptions {
|
|
|
56
56
|
maxPollBatches?: number;
|
|
57
57
|
/** Prefer one advertised delivery mode when the host can provide it. */
|
|
58
58
|
preferredDelivery?: 'poll' | 'push' | 'webhook';
|
|
59
|
+
/** Versioned MCP Events wire profile. Defaults to auto/current experimental snapshot. */
|
|
60
|
+
compatibilityProfile?: 'auto' | 'experimental-extension-2026-09-25' | string;
|
|
59
61
|
/** Host-owned adapter for events/stream delivery. */
|
|
60
62
|
openEventStream?: HostMcpEventDeliveryOpener;
|
|
61
63
|
/** Host-owned adapter for webhook subscription/receiver delivery. */
|
|
@@ -81,11 +83,37 @@ export type HostWakeHandler = (
|
|
|
81
83
|
activation?: ActivationEnvelope,
|
|
82
84
|
) => Promise<HostWakeReceipt | string> | HostWakeReceipt | string;
|
|
83
85
|
|
|
86
|
+
export interface EventIntelligenceStoreCapabilities {
|
|
87
|
+
version: string;
|
|
88
|
+
sharedState: 'none' | 'single-process' | 'strong' | string;
|
|
89
|
+
scopeIsolation: 'unknown' | 'strong' | string;
|
|
90
|
+
wakeClaims: 'unknown' | 'process-atomic' | 'distributed-atomic' | string;
|
|
91
|
+
partitionLeases: 'none' | 'process-atomic' | 'distributed-atomic' | string;
|
|
92
|
+
mutableCompaction: 'none' | 'atomic-snapshot' | string;
|
|
93
|
+
readModel: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
84
96
|
export interface EventIntelligenceStore {
|
|
85
97
|
init?(): Promise<unknown>;
|
|
98
|
+
storeCapabilities?(): EventIntelligenceStoreCapabilities;
|
|
86
99
|
/** Required when non-default scopes are used. Implementations should return an isolated store view. */
|
|
87
100
|
forScope?(scopeId: string): Promise<EventIntelligenceStore> | EventIntelligenceStore;
|
|
88
101
|
listScopeIds?(): Promise<string[]> | string[];
|
|
102
|
+
claimPartitionLease?(partitionKey: string, options: {
|
|
103
|
+
workerId: string;
|
|
104
|
+
now?: string;
|
|
105
|
+
leaseMs?: number;
|
|
106
|
+
}): Promise<unknown | null>;
|
|
107
|
+
renewPartitionLease?(partitionKey: string, options: {
|
|
108
|
+
workerId: string;
|
|
109
|
+
now?: string;
|
|
110
|
+
leaseMs?: number;
|
|
111
|
+
}): Promise<unknown | null>;
|
|
112
|
+
releasePartitionLease?(partitionKey: string, options: {
|
|
113
|
+
workerId: string;
|
|
114
|
+
now?: string;
|
|
115
|
+
}): Promise<boolean>;
|
|
116
|
+
compactMutableState?(): Promise<unknown>;
|
|
89
117
|
[key: string]: any;
|
|
90
118
|
}
|
|
91
119
|
|
|
@@ -112,21 +140,26 @@ export interface EventIntelligenceScopedHost {
|
|
|
112
140
|
readonly triggerInspector: any;
|
|
113
141
|
readonly triggerPlanner: any;
|
|
114
142
|
readonly activationHydrator: any;
|
|
115
|
-
readonly eventSources: any[];
|
|
143
|
+
readonly eventSources: Promise<any[]> | any[];
|
|
144
|
+
readonly storeCapabilities?: EventIntelligenceStoreCapabilities;
|
|
145
|
+
describeTriggerLanguage(input?: {
|
|
146
|
+
category?: 'predicates' | 'pattern' | 'windows' | 'aggregates' | 'state' | 'selection' | 'semantic' | 'execution' | 'timing' | 'lifecycle';
|
|
147
|
+
operator?: string;
|
|
148
|
+
}): Record<string, unknown>;
|
|
116
149
|
planTrigger(input: TriggerPlanInput): Promise<{
|
|
117
|
-
planVersion: '
|
|
150
|
+
planVersion: '2';
|
|
118
151
|
definition: any;
|
|
119
152
|
connectionIds: string[];
|
|
120
153
|
resolvedSources: any[];
|
|
121
154
|
warnings: any[];
|
|
122
155
|
explanation: any;
|
|
123
156
|
}>;
|
|
124
|
-
hydrateWake(wakeId: string): ActivationEnvelope;
|
|
157
|
+
hydrateWake(wakeId: string): Promise<ActivationEnvelope> | ActivationEnvelope;
|
|
125
158
|
attachMcpClient(connection: HostMcpConnectionOptions): Promise<{
|
|
126
159
|
connectionId: string;
|
|
127
160
|
events: any[];
|
|
128
161
|
}>;
|
|
129
|
-
mcpStatus(): any[];
|
|
162
|
+
mcpStatus(): Promise<any[]> | any[];
|
|
130
163
|
}
|
|
131
164
|
|
|
132
165
|
export interface EventIntelligenceHost extends EventIntelligenceScopedHost {
|
|
@@ -154,6 +187,7 @@ export function createHostMcpEventsConnection(
|
|
|
154
187
|
maxEvents: number;
|
|
155
188
|
maxPollBatches: number;
|
|
156
189
|
preferredDelivery: 'poll' | 'push' | 'webhook' | null;
|
|
190
|
+
compatibilityProfile: string;
|
|
157
191
|
openEventStream: HostMcpEventDeliveryOpener | null;
|
|
158
192
|
createWebhookSubscription: HostMcpEventDeliveryOpener | null;
|
|
159
193
|
};
|
|
@@ -163,6 +197,22 @@ export function createEventIntelligenceHost(
|
|
|
163
197
|
): Promise<EventIntelligenceHost>;
|
|
164
198
|
|
|
165
199
|
|
|
200
|
+
export function describeStoreCapabilities(
|
|
201
|
+
store: EventIntelligenceStore,
|
|
202
|
+
): EventIntelligenceStoreCapabilities;
|
|
203
|
+
|
|
204
|
+
export function validateSharedStoreCapabilities(
|
|
205
|
+
store: EventIntelligenceStore,
|
|
206
|
+
): {
|
|
207
|
+
ok: boolean;
|
|
208
|
+
capabilities: EventIntelligenceStoreCapabilities;
|
|
209
|
+
errors: string[];
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export function assertSharedStoreCapabilities(
|
|
213
|
+
store: EventIntelligenceStore,
|
|
214
|
+
): EventIntelligenceStoreCapabilities;
|
|
215
|
+
|
|
166
216
|
export class PersistentEventStore implements EventIntelligenceStore {
|
|
167
217
|
constructor(dataDir: string);
|
|
168
218
|
readonly dataDir: string;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
describeTriggerLanguage,
|
|
3
|
+
} from '../dist/src/intelligenceProtocol/index.js';
|
|
1
4
|
import {
|
|
2
5
|
createLocalEventIntelligenceRuntime,
|
|
3
6
|
} from './lib/local-event-intelligence-runtime.mjs';
|
|
@@ -9,6 +12,11 @@ import {
|
|
|
9
12
|
PersistentEventStore,
|
|
10
13
|
normalizeEventScopeId,
|
|
11
14
|
} from './lib/persistent-event-store.mjs';
|
|
15
|
+
import {
|
|
16
|
+
assertSharedStoreCapabilities,
|
|
17
|
+
describeStoreCapabilities,
|
|
18
|
+
validateSharedStoreCapabilities,
|
|
19
|
+
} from './lib/store-capabilities.mjs';
|
|
12
20
|
|
|
13
21
|
function normalizeMcpConnection(input) {
|
|
14
22
|
const connectionId = String(
|
|
@@ -72,6 +80,7 @@ function scopedHostView(runtime, context) {
|
|
|
72
80
|
scopeId,
|
|
73
81
|
runtime: context,
|
|
74
82
|
store: context.store,
|
|
83
|
+
storeCapabilities: describeStoreCapabilities(context.store),
|
|
75
84
|
triggerControl: context.triggerControl,
|
|
76
85
|
triggerInspector: context.triggerInspector,
|
|
77
86
|
triggerPlanner: context.triggerPlanner,
|
|
@@ -79,6 +88,9 @@ function scopedHostView(runtime, context) {
|
|
|
79
88
|
planTrigger(input) {
|
|
80
89
|
return context.triggerPlanner.plan(input);
|
|
81
90
|
},
|
|
91
|
+
describeTriggerLanguage(input = {}) {
|
|
92
|
+
return describeTriggerLanguage(input);
|
|
93
|
+
},
|
|
82
94
|
hydrateWake(wakeId) {
|
|
83
95
|
return context.activationHydrator.hydrateWake(wakeId);
|
|
84
96
|
},
|
|
@@ -92,8 +104,8 @@ function scopedHostView(runtime, context) {
|
|
|
92
104
|
});
|
|
93
105
|
return runtime.mcpEventsClient.attachConnection(normalized);
|
|
94
106
|
},
|
|
95
|
-
mcpStatus() {
|
|
96
|
-
return runtime.mcpEventsClient.status()
|
|
107
|
+
async mcpStatus() {
|
|
108
|
+
return (await runtime.mcpEventsClient.status())
|
|
97
109
|
.filter((entry) => entry.scopeId === scopeId);
|
|
98
110
|
},
|
|
99
111
|
};
|
|
@@ -225,6 +237,9 @@ export async function createEventIntelligenceHost({
|
|
|
225
237
|
get store() {
|
|
226
238
|
return runtime.store;
|
|
227
239
|
},
|
|
240
|
+
get storeCapabilities() {
|
|
241
|
+
return runtime.storeCapabilities;
|
|
242
|
+
},
|
|
228
243
|
get triggerControl() {
|
|
229
244
|
return runtime.triggerControl;
|
|
230
245
|
},
|
|
@@ -240,6 +255,9 @@ export async function createEventIntelligenceHost({
|
|
|
240
255
|
planTrigger(input) {
|
|
241
256
|
return runtime.triggerPlanner.plan(input);
|
|
242
257
|
},
|
|
258
|
+
describeTriggerLanguage(input = {}) {
|
|
259
|
+
return describeTriggerLanguage(input);
|
|
260
|
+
},
|
|
243
261
|
hydrateWake(wakeId) {
|
|
244
262
|
return runtime.activationHydrator.hydrateWake(wakeId);
|
|
245
263
|
},
|
|
@@ -255,8 +273,8 @@ export async function createEventIntelligenceHost({
|
|
|
255
273
|
registryManaged.delete(connectionId);
|
|
256
274
|
return runtime.mcpEventsClient.detachConnection(connectionId);
|
|
257
275
|
},
|
|
258
|
-
mcpStatus() {
|
|
259
|
-
return runtime.mcpEventsClient.status();
|
|
276
|
+
async mcpStatus() {
|
|
277
|
+
return await runtime.mcpEventsClient.status();
|
|
260
278
|
},
|
|
261
279
|
loadedScopes() {
|
|
262
280
|
return runtime.loadedScopes();
|
|
@@ -274,6 +292,9 @@ export async function createEventIntelligenceHost({
|
|
|
274
292
|
}
|
|
275
293
|
|
|
276
294
|
export {
|
|
295
|
+
assertSharedStoreCapabilities,
|
|
277
296
|
createHostMcpEventsConnection,
|
|
297
|
+
describeStoreCapabilities,
|
|
278
298
|
PersistentEventStore,
|
|
299
|
+
validateSharedStoreCapabilities,
|
|
279
300
|
};
|
|
@@ -2,21 +2,21 @@ import {
|
|
|
2
2
|
parseActivationEnvelope,
|
|
3
3
|
} from '../../dist/src/intelligenceProtocol/index.js';
|
|
4
4
|
|
|
5
|
-
function latestMatchForWake(store, wake, delivery) {
|
|
5
|
+
async function latestMatchForWake(store, wake, delivery) {
|
|
6
6
|
const matchId =
|
|
7
7
|
delivery?.matchId ||
|
|
8
8
|
(wake?.subscriptionId?.startsWith('trigger:') ? wake.sourceEventId : null);
|
|
9
9
|
if (!matchId) return null;
|
|
10
10
|
|
|
11
|
-
return store.listTriggerMatches()
|
|
11
|
+
return (await store.listTriggerMatches())
|
|
12
12
|
.find((candidate) => candidate.matchId === matchId) ?? null;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
function definitionForMatch(store, match, delivery) {
|
|
15
|
+
async function definitionForMatch(store, match, delivery) {
|
|
16
16
|
if (!match && !delivery) return null;
|
|
17
17
|
const triggerId = match?.triggerId ?? delivery?.triggerId;
|
|
18
18
|
const triggerVersion = match?.triggerVersion ?? delivery?.triggerVersion;
|
|
19
|
-
return store.listTriggers().find((candidate) =>
|
|
19
|
+
return (await store.listTriggers()).find((candidate) =>
|
|
20
20
|
candidate.triggerId === triggerId &&
|
|
21
21
|
candidate.version === triggerVersion
|
|
22
22
|
) ?? null;
|
|
@@ -42,7 +42,7 @@ export class ActivationHydrator {
|
|
|
42
42
|
this.store = store;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
hydrateWake(wakeIdInput) {
|
|
45
|
+
async hydrateWake(wakeIdInput) {
|
|
46
46
|
const wakeId = String(wakeIdInput || '').trim();
|
|
47
47
|
if (!wakeId) {
|
|
48
48
|
const error = new Error('wakeId is required');
|
|
@@ -50,7 +50,7 @@ export class ActivationHydrator {
|
|
|
50
50
|
throw error;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
const wake = this.store.latestWake(wakeId);
|
|
53
|
+
const wake = await this.store.latestWake(wakeId);
|
|
54
54
|
if (!wake) {
|
|
55
55
|
const error = new Error(`Unknown wake: ${wakeId}`);
|
|
56
56
|
error.code = 'ACTIVATION_WAKE_NOT_FOUND';
|
|
@@ -59,9 +59,9 @@ export class ActivationHydrator {
|
|
|
59
59
|
|
|
60
60
|
const delivery =
|
|
61
61
|
typeof this.store.getWakeDelivery === 'function'
|
|
62
|
-
? this.store.getWakeDelivery(wakeId)
|
|
62
|
+
? await this.store.getWakeDelivery(wakeId)
|
|
63
63
|
: null;
|
|
64
|
-
const match = latestMatchForWake(this.store, wake, delivery);
|
|
64
|
+
const match = await latestMatchForWake(this.store, wake, delivery);
|
|
65
65
|
if (!match) {
|
|
66
66
|
const error = new Error(
|
|
67
67
|
`Wake ${wakeId} is not backed by a composite trigger match`,
|
|
@@ -70,7 +70,7 @@ export class ActivationHydrator {
|
|
|
70
70
|
throw error;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const definition = definitionForMatch(this.store, match, delivery);
|
|
73
|
+
const definition = await definitionForMatch(this.store, match, delivery);
|
|
74
74
|
if (!definition?.target) {
|
|
75
75
|
const error = new Error(
|
|
76
76
|
`Trigger definition missing for wake ${wakeId}`,
|
|
@@ -100,12 +100,13 @@ export class ActivationHydrator {
|
|
|
100
100
|
eventName: event.eventName,
|
|
101
101
|
traceId: event.traceId,
|
|
102
102
|
occurredAt: event.occurredAt,
|
|
103
|
+
...(event.receivedAt ? { receivedAt: event.receivedAt } : {}),
|
|
103
104
|
payloadHash: event.payloadHash ?? null,
|
|
104
105
|
...(includeData ? { data: event.data } : {}),
|
|
105
106
|
}));
|
|
106
107
|
|
|
107
108
|
return parseActivationEnvelope({
|
|
108
|
-
activationVersion: '
|
|
109
|
+
activationVersion: '2',
|
|
109
110
|
wake: {
|
|
110
111
|
wakeId,
|
|
111
112
|
status: wake.status,
|
|
@@ -117,14 +118,14 @@ export class ActivationHydrator {
|
|
|
117
118
|
triggerId: definition.triggerId,
|
|
118
119
|
version: definition.version,
|
|
119
120
|
description: definition.description ?? null,
|
|
120
|
-
|
|
121
|
+
pattern: definition.pattern,
|
|
121
122
|
lifecycle: definition.lifecycle ?? {},
|
|
122
123
|
},
|
|
123
124
|
continuation,
|
|
124
125
|
match: {
|
|
125
126
|
matchId: match.matchId,
|
|
126
127
|
status: match.status,
|
|
127
|
-
|
|
128
|
+
partitionKey: match.partitionKey,
|
|
128
129
|
openedAt: match.openedAt,
|
|
129
130
|
updatedAt: match.updatedAt,
|
|
130
131
|
},
|
|
@@ -9,7 +9,6 @@ export class CompositeEventConsumer {
|
|
|
9
9
|
wakeCoordinator = null,
|
|
10
10
|
wakeCoordinators = null,
|
|
11
11
|
derivedEventCoordinator = null,
|
|
12
|
-
processor = null,
|
|
13
12
|
maxDerivedDepth = 16,
|
|
14
13
|
}) {
|
|
15
14
|
this.store = store;
|
|
@@ -19,7 +18,6 @@ export class CompositeEventConsumer {
|
|
|
19
18
|
? wakeCoordinators
|
|
20
19
|
: new Map();
|
|
21
20
|
this.derivedEventCoordinator = derivedEventCoordinator;
|
|
22
|
-
this.processor = processor;
|
|
23
21
|
this.maxDerivedDepth = Math.max(1, Number(maxDerivedDepth) || 16);
|
|
24
22
|
}
|
|
25
23
|
|
|
@@ -35,6 +33,7 @@ export class CompositeEventConsumer {
|
|
|
35
33
|
provider,
|
|
36
34
|
traceId,
|
|
37
35
|
subscriptionArguments,
|
|
36
|
+
receivedAt,
|
|
38
37
|
}) {
|
|
39
38
|
const correlatable = await mcpOccurrenceToCorrelatableEvent(
|
|
40
39
|
event,
|
|
@@ -43,6 +42,7 @@ export class CompositeEventConsumer {
|
|
|
43
42
|
...(provider ? { provider } : {}),
|
|
44
43
|
...(serverId ? { serverId } : {}),
|
|
45
44
|
subscriptionArguments: subscriptionArguments ?? {},
|
|
45
|
+
...(receivedAt ? { receivedAt } : {}),
|
|
46
46
|
},
|
|
47
47
|
);
|
|
48
48
|
|
|
@@ -58,7 +58,7 @@ export class CompositeEventConsumer {
|
|
|
58
58
|
for (const result of results) {
|
|
59
59
|
if (!result.matched || !result.match) continue;
|
|
60
60
|
|
|
61
|
-
const definition = this.store.listTriggers().find(
|
|
61
|
+
const definition = (await this.store.listTriggers()).find(
|
|
62
62
|
(candidate) =>
|
|
63
63
|
candidate.triggerId === result.match.triggerId &&
|
|
64
64
|
candidate.version === result.match.triggerVersion,
|
|
@@ -137,31 +137,13 @@ export class CompositeEventConsumer {
|
|
|
137
137
|
const delivery = await coordinator.deliverMatched(result.match);
|
|
138
138
|
const persistedWake = delivery.wake;
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
persistedWake.runtimeReceiptId,
|
|
148
|
-
);
|
|
149
|
-
deliveries.push({
|
|
150
|
-
triggerId: result.triggerId,
|
|
151
|
-
matchId: result.match.matchId,
|
|
152
|
-
wakeId: handled.wakeId,
|
|
153
|
-
status: handled.status,
|
|
154
|
-
runtimeReceiptId: handled.runtimeReceiptId ?? null,
|
|
155
|
-
});
|
|
156
|
-
} else {
|
|
157
|
-
deliveries.push({
|
|
158
|
-
triggerId: result.triggerId,
|
|
159
|
-
matchId: result.match.matchId,
|
|
160
|
-
wakeId: persistedWake?.wakeId ?? null,
|
|
161
|
-
status: delivery.status,
|
|
162
|
-
runtimeReceiptId: persistedWake?.runtimeReceiptId ?? null,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
140
|
+
deliveries.push({
|
|
141
|
+
triggerId: result.triggerId,
|
|
142
|
+
matchId: result.match.matchId,
|
|
143
|
+
wakeId: persistedWake?.wakeId ?? null,
|
|
144
|
+
status: delivery.status,
|
|
145
|
+
runtimeReceiptId: persistedWake?.runtimeReceiptId ?? null,
|
|
146
|
+
});
|
|
165
147
|
}
|
|
166
148
|
|
|
167
149
|
return {
|
|
@@ -64,11 +64,11 @@ export class CompositeWakeCoordinator {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
async deliveredResult({ match, wakeId, definition, delivery }) {
|
|
67
|
-
const existing = this.store.latestWake(wakeId);
|
|
67
|
+
const existing = await this.store.latestWake(wakeId);
|
|
68
68
|
let delivered = existing;
|
|
69
69
|
if (
|
|
70
70
|
!delivered ||
|
|
71
|
-
|
|
71
|
+
delivered.status !== 'delivered'
|
|
72
72
|
) {
|
|
73
73
|
delivered = WakeRecordSchema.parse({
|
|
74
74
|
wakeId,
|
|
@@ -85,7 +85,7 @@ export class CompositeWakeCoordinator {
|
|
|
85
85
|
await this.store.appendAudit({
|
|
86
86
|
auditId: await stableId(
|
|
87
87
|
'audit',
|
|
88
|
-
`${wakeId}:delivered-recovered:${this.store.auditLength()}`,
|
|
88
|
+
`${wakeId}:delivered-recovered:${await this.store.auditLength()}`,
|
|
89
89
|
),
|
|
90
90
|
traceId: delivered.traceId,
|
|
91
91
|
timestamp: this.now().toISOString(),
|
|
@@ -108,7 +108,7 @@ export class CompositeWakeCoordinator {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
return {
|
|
111
|
-
status:
|
|
111
|
+
status: 'wake_delivered',
|
|
112
112
|
wake: delivered,
|
|
113
113
|
};
|
|
114
114
|
}
|
|
@@ -120,7 +120,7 @@ export class CompositeWakeCoordinator {
|
|
|
120
120
|
|
|
121
121
|
if (match.status === 'fired') {
|
|
122
122
|
const existing = match.firedWakeId
|
|
123
|
-
? this.store.latestWake(match.firedWakeId)
|
|
123
|
+
? await this.store.latestWake(match.firedWakeId)
|
|
124
124
|
: null;
|
|
125
125
|
return {
|
|
126
126
|
status: 'already_fired',
|
|
@@ -128,7 +128,7 @@ export class CompositeWakeCoordinator {
|
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
const definition = this.store.listTriggers().find(
|
|
131
|
+
const definition = (await this.store.listTriggers()).find(
|
|
132
132
|
(candidate) =>
|
|
133
133
|
candidate.triggerId === match.triggerId &&
|
|
134
134
|
candidate.version === match.triggerVersion,
|
|
@@ -148,13 +148,13 @@ export class CompositeWakeCoordinator {
|
|
|
148
148
|
`composite:${match.matchId}:${definition.target.runtime}:${definition.target.id}`,
|
|
149
149
|
);
|
|
150
150
|
|
|
151
|
-
const existingWake = this.store.latestWake(wakeId);
|
|
152
|
-
if (existingWake?.status === '
|
|
151
|
+
const existingWake = await this.store.latestWake(wakeId);
|
|
152
|
+
if (existingWake?.status === 'delivered') {
|
|
153
153
|
if (match.status !== 'fired') {
|
|
154
154
|
await this.triggerEngine.markFired(match.matchId, wakeId);
|
|
155
155
|
}
|
|
156
156
|
return {
|
|
157
|
-
status:
|
|
157
|
+
status: 'wake_delivered',
|
|
158
158
|
wake: existingWake,
|
|
159
159
|
};
|
|
160
160
|
}
|
|
@@ -179,7 +179,7 @@ export class CompositeWakeCoordinator {
|
|
|
179
179
|
await this.store.appendAudit({
|
|
180
180
|
auditId: await stableId(
|
|
181
181
|
'audit',
|
|
182
|
-
`${wakeId}:queued:${this.store.auditLength()}`,
|
|
182
|
+
`${wakeId}:queued:${await this.store.auditLength()}`,
|
|
183
183
|
),
|
|
184
184
|
traceId: queued.traceId,
|
|
185
185
|
timestamp: this.now().toISOString(),
|
|
@@ -196,7 +196,7 @@ export class CompositeWakeCoordinator {
|
|
|
196
196
|
}
|
|
197
197
|
return {
|
|
198
198
|
status: 'wake_queued',
|
|
199
|
-
wake: this.store.latestWake(wakeId) ?? queued,
|
|
199
|
+
wake: await this.store.latestWake(wakeId) ?? queued,
|
|
200
200
|
};
|
|
201
201
|
}
|
|
202
202
|
|
|
@@ -210,7 +210,7 @@ export class CompositeWakeCoordinator {
|
|
|
210
210
|
now: nowIso,
|
|
211
211
|
});
|
|
212
212
|
|
|
213
|
-
const currentDelivery = this.store.getWakeDelivery(wakeId);
|
|
213
|
+
const currentDelivery = await this.store.getWakeDelivery(wakeId);
|
|
214
214
|
if (currentDelivery?.status === 'delivered') {
|
|
215
215
|
return this.deliveredResult({
|
|
216
216
|
match,
|
|
@@ -240,25 +240,25 @@ export class CompositeWakeCoordinator {
|
|
|
240
240
|
});
|
|
241
241
|
|
|
242
242
|
if (!claim) {
|
|
243
|
-
const delivery = this.store.getWakeDelivery(wakeId);
|
|
243
|
+
const delivery = await this.store.getWakeDelivery(wakeId);
|
|
244
244
|
return {
|
|
245
245
|
status:
|
|
246
246
|
delivery?.status === 'claimed'
|
|
247
247
|
? 'delivery_in_progress'
|
|
248
248
|
: 'retry_scheduled',
|
|
249
|
-
wake: this.store.latestWake(wakeId) ?? queued,
|
|
249
|
+
wake: await this.store.latestWake(wakeId) ?? queued,
|
|
250
250
|
delivery,
|
|
251
251
|
};
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
let persistedQueued = this.store.latestWake(wakeId);
|
|
254
|
+
let persistedQueued = await this.store.latestWake(wakeId);
|
|
255
255
|
if (!persistedQueued) {
|
|
256
256
|
await this.store.appendWake(queued);
|
|
257
257
|
persistedQueued = queued;
|
|
258
258
|
await this.store.appendAudit({
|
|
259
259
|
auditId: await stableId(
|
|
260
260
|
'audit',
|
|
261
|
-
`${wakeId}:queued:${this.store.auditLength()}`,
|
|
261
|
+
`${wakeId}:queued:${await this.store.auditLength()}`,
|
|
262
262
|
),
|
|
263
263
|
traceId: queued.traceId,
|
|
264
264
|
timestamp: this.now().toISOString(),
|
|
@@ -305,7 +305,7 @@ export class CompositeWakeCoordinator {
|
|
|
305
305
|
await this.store.appendAudit({
|
|
306
306
|
auditId: await stableId(
|
|
307
307
|
'audit',
|
|
308
|
-
`${wakeId}:delivered:${this.store.auditLength()}`,
|
|
308
|
+
`${wakeId}:delivered:${await this.store.auditLength()}`,
|
|
309
309
|
),
|
|
310
310
|
traceId: delivered.traceId,
|
|
311
311
|
timestamp: this.now().toISOString(),
|
|
@@ -350,7 +350,7 @@ export class CompositeWakeCoordinator {
|
|
|
350
350
|
await this.store.appendAudit({
|
|
351
351
|
auditId: await stableId(
|
|
352
352
|
'audit',
|
|
353
|
-
`${wakeId}:retry:${delivery?.attemptCount ?? claim.attemptCount}:${this.store.auditLength()}`,
|
|
353
|
+
`${wakeId}:retry:${delivery?.attemptCount ?? claim.attemptCount}:${await this.store.auditLength()}`,
|
|
354
354
|
),
|
|
355
355
|
traceId: queued.traceId,
|
|
356
356
|
timestamp: this.now().toISOString(),
|
|
@@ -382,7 +382,7 @@ export class CompositeWakeCoordinator {
|
|
|
382
382
|
await this.store.appendAudit({
|
|
383
383
|
auditId: await stableId(
|
|
384
384
|
'audit',
|
|
385
|
-
`${wakeId}:dead-letter:${this.store.auditLength()}`,
|
|
385
|
+
`${wakeId}:dead-letter:${await this.store.auditLength()}`,
|
|
386
386
|
),
|
|
387
387
|
traceId: deadLetter.traceId,
|
|
388
388
|
timestamp: this.now().toISOString(),
|
|
@@ -42,11 +42,11 @@ export class DerivedContractRegistry {
|
|
|
42
42
|
return sha256Hex(canonicalize(payloadSchema ?? {}));
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
list(eventName) {
|
|
45
|
+
async list(eventName) {
|
|
46
46
|
return this.store.listDerivedContracts(eventName);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
get(eventName, contractVersion) {
|
|
49
|
+
async get(eventName, contractVersion) {
|
|
50
50
|
return this.store.getDerivedContract(eventName, contractVersion);
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -57,7 +57,7 @@ export class DerivedContractRegistry {
|
|
|
57
57
|
}) {
|
|
58
58
|
const canonicalSchema = canonicalize(payloadSchema ?? {});
|
|
59
59
|
const schemaFingerprint = await this.fingerprint(canonicalSchema);
|
|
60
|
-
const existing = this.get(eventName, contractVersion);
|
|
60
|
+
const existing = await this.get(eventName, contractVersion);
|
|
61
61
|
if (
|
|
62
62
|
existing &&
|
|
63
63
|
existing.schemaFingerprint !== schemaFingerprint
|
|
@@ -89,7 +89,7 @@ export class DerivedContractRegistry {
|
|
|
89
89
|
}) {
|
|
90
90
|
const canonicalSchema = canonicalize(payloadSchema ?? {});
|
|
91
91
|
const schemaFingerprint = await this.fingerprint(canonicalSchema);
|
|
92
|
-
const existing = this.get(eventName, contractVersion);
|
|
92
|
+
const existing = await this.get(eventName, contractVersion);
|
|
93
93
|
|
|
94
94
|
if (
|
|
95
95
|
existing &&
|