mcp-event-intelligence 0.5.0 → 0.6.1
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 +53 -0
- package/README.md +31 -38
- package/SECURITY.md +4 -4
- package/bin/mcp-event-intelligence.mjs +2 -2
- package/dist/src/composite/engine.d.ts +11 -7
- package/dist/src/composite/engine.js +152 -414
- package/dist/src/composite/matching.d.ts +1 -5
- package/dist/src/composite/matching.js +0 -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 +36 -3
- package/dist/src/intelligenceProtocol/activationSchemas.js +4 -3
- package/dist/src/intelligenceProtocol/index.d.ts +8 -12
- package/dist/src/intelligenceProtocol/index.js +4 -7
- package/dist/src/intelligenceProtocol/lifecycle.js +2 -10
- package/dist/src/intelligenceProtocol/operatorRegistry.d.ts +3 -270
- package/dist/src/intelligenceProtocol/operatorRegistry.js +5 -144
- package/dist/src/intelligenceProtocol/schemas.d.ts +0 -189
- package/dist/src/intelligenceProtocol/schemas.js +1 -97
- package/dist/src/intelligenceProtocol/triggerPlanSchemas.d.ts +5 -102
- package/dist/src/intelligenceProtocol/triggerPlanSchemas.js +2 -26
- package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +14 -215
- package/dist/src/intelligenceProtocol/triggerSchemas.js +18 -191
- package/dist/src/intelligenceProtocol/version.d.ts +2 -2
- package/dist/src/intelligenceProtocol/version.js +2 -2
- package/dist/src/patternV2/evaluator.d.ts +9 -1
- package/dist/src/patternV2/evaluator.js +63 -4
- 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 +5 -2
- package/scripts/host-integration.d.mts +48 -5
- package/scripts/host-integration.mjs +16 -4
- package/scripts/lib/activation-hydrator.mjs +12 -12
- package/scripts/lib/composite-event-consumer.mjs +8 -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 +11 -22
- package/scripts/lib/local-event-intelligence-runtime.mjs +35 -3
- package/scripts/lib/mcp-events-client.mjs +36 -32
- package/scripts/lib/persistent-event-store.mjs +228 -61
- package/scripts/lib/store-capabilities.mjs +88 -0
- package/scripts/lib/store-conformance.mjs +152 -0
- package/scripts/lib/temporal-deadline-scheduler.mjs +3 -3
- package/scripts/lib/trigger-control-plane.mjs +52 -57
- package/scripts/lib/trigger-inspector.mjs +48 -160
- package/scripts/lib/trigger-planner.mjs +37 -126
- package/scripts/lib/wake-retry-scheduler.mjs +2 -19
- package/scripts/mcp-stdio-server.mjs +8 -8
- package/scripts/service.mjs +19 -138
- package/server.json +3 -3
- 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/dist/src/patternV2/legacyCompiler.d.ts +0 -3
- package/dist/src/patternV2/legacyCompiler.js +0 -184
- package/scripts/lib/event-processor.mjs +0 -624
- package/scripts/lib/http-wake-deliverer.mjs +0 -31
|
@@ -13,6 +13,33 @@ function stableValue(value) {
|
|
|
13
13
|
}
|
|
14
14
|
return value;
|
|
15
15
|
}
|
|
16
|
+
function durableSemanticCacheKey(context, node, candidate) {
|
|
17
|
+
const evaluatorIdentity = String(context.evaluator?.cacheIdentity ?? '').trim();
|
|
18
|
+
if (!evaluatorIdentity || !context.semanticCacheNamespace) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const payload = stableValue({
|
|
22
|
+
schema: 'pattern-v2-semantic-cache.v1',
|
|
23
|
+
namespace: context.semanticCacheNamespace,
|
|
24
|
+
evaluator: evaluatorIdentity,
|
|
25
|
+
node: {
|
|
26
|
+
id: node.id,
|
|
27
|
+
refs: node.refs,
|
|
28
|
+
instruction: node.instruction,
|
|
29
|
+
input: node.input,
|
|
30
|
+
matchThreshold: node.matchThreshold,
|
|
31
|
+
rejectThreshold: node.rejectThreshold,
|
|
32
|
+
uncertain: node.uncertain,
|
|
33
|
+
},
|
|
34
|
+
candidate: candidate.events.map((event) => ({
|
|
35
|
+
clauseId: event.clauseId,
|
|
36
|
+
serverId: event.serverId ?? null,
|
|
37
|
+
sourceEventId: event.sourceEventId,
|
|
38
|
+
subscriptionArguments: event.subscriptionArguments ?? {},
|
|
39
|
+
})),
|
|
40
|
+
});
|
|
41
|
+
return JSON.stringify(payload);
|
|
42
|
+
}
|
|
16
43
|
function eventIdentity(event) {
|
|
17
44
|
return [
|
|
18
45
|
event.serverId ?? '-',
|
|
@@ -843,13 +870,35 @@ async function evaluateNode(node, context) {
|
|
|
843
870
|
context.truncated = true;
|
|
844
871
|
break;
|
|
845
872
|
}
|
|
846
|
-
const
|
|
873
|
+
const localCacheKey = [
|
|
847
874
|
node.id,
|
|
848
875
|
candidateSignature(candidate),
|
|
849
876
|
node.instruction,
|
|
877
|
+
JSON.stringify(node.input),
|
|
878
|
+
node.matchThreshold,
|
|
879
|
+
node.rejectThreshold,
|
|
880
|
+
node.uncertain,
|
|
850
881
|
].join(':');
|
|
851
|
-
|
|
882
|
+
const durableCacheKey = node.execution.cache
|
|
883
|
+
? durableSemanticCacheKey(context, node, candidate)
|
|
884
|
+
: null;
|
|
885
|
+
let decision = context.semanticCache.get(localCacheKey);
|
|
886
|
+
if (decision) {
|
|
887
|
+
context.semanticCacheHits += 1;
|
|
888
|
+
}
|
|
889
|
+
if (!decision &&
|
|
890
|
+
durableCacheKey &&
|
|
891
|
+
context.durableSemanticCache) {
|
|
892
|
+
decision =
|
|
893
|
+
await context.durableSemanticCache.get(durableCacheKey) ??
|
|
894
|
+
undefined;
|
|
895
|
+
if (decision) {
|
|
896
|
+
context.semanticCacheHits += 1;
|
|
897
|
+
context.semanticCache.set(localCacheKey, decision);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
852
900
|
if (!decision) {
|
|
901
|
+
context.semanticCacheMisses += 1;
|
|
853
902
|
context.semanticEvaluations += 1;
|
|
854
903
|
decision = await withTimeout(new SemanticConditionEngine(context.evaluator).evaluate(semanticSource(candidate, node.refs), {
|
|
855
904
|
type: 'semantic_boolean',
|
|
@@ -860,7 +909,11 @@ async function evaluateNode(node, context) {
|
|
|
860
909
|
uncertain: node.uncertain,
|
|
861
910
|
}), node.execution.timeoutMs, `Semantic node ${node.id}`);
|
|
862
911
|
if (node.execution.cache) {
|
|
863
|
-
context.semanticCache.set(
|
|
912
|
+
context.semanticCache.set(localCacheKey, decision);
|
|
913
|
+
if (durableCacheKey &&
|
|
914
|
+
context.durableSemanticCache) {
|
|
915
|
+
await context.durableSemanticCache.set(durableCacheKey, decision);
|
|
916
|
+
}
|
|
864
917
|
}
|
|
865
918
|
}
|
|
866
919
|
context.semanticTrace.push({
|
|
@@ -893,7 +946,7 @@ export function evaluatePatternMeasure(candidate, measure) {
|
|
|
893
946
|
}
|
|
894
947
|
return evaluateValue(expression, candidate);
|
|
895
948
|
}
|
|
896
|
-
export async function evaluatePatternV2({ definition, events, evaluator = null, now = new Date(), allowedLatenessMs = 0, }) {
|
|
949
|
+
export async function evaluatePatternV2({ definition, events, evaluator = null, now = new Date(), allowedLatenessMs = 0, semanticCache = null, semanticCacheNamespace = '', }) {
|
|
897
950
|
const ordered = [...events].sort(eventOrder);
|
|
898
951
|
const context = {
|
|
899
952
|
events: ordered,
|
|
@@ -902,8 +955,12 @@ export async function evaluatePatternV2({ definition, events, evaluator = null,
|
|
|
902
955
|
maxCandidates: definition.execution.maxCandidates,
|
|
903
956
|
maxSemanticEvaluations: definition.execution.maxSemanticEvaluations,
|
|
904
957
|
semanticEvaluations: 0,
|
|
958
|
+
semanticCacheHits: 0,
|
|
959
|
+
semanticCacheMisses: 0,
|
|
905
960
|
truncated: false,
|
|
906
961
|
semanticCache: new Map(),
|
|
962
|
+
durableSemanticCache: semanticCache,
|
|
963
|
+
semanticCacheNamespace,
|
|
907
964
|
semanticTrace: [],
|
|
908
965
|
allowedLatenessMs,
|
|
909
966
|
};
|
|
@@ -918,6 +975,8 @@ export async function evaluatePatternV2({ definition, events, evaluator = null,
|
|
|
918
975
|
matches,
|
|
919
976
|
pending,
|
|
920
977
|
semanticEvaluations: context.semanticEvaluations,
|
|
978
|
+
semanticCacheHits: context.semanticCacheHits,
|
|
979
|
+
semanticCacheMisses: context.semanticCacheMisses,
|
|
921
980
|
semanticTrace: context.semanticTrace,
|
|
922
981
|
truncated: context.truncated,
|
|
923
982
|
};
|
|
@@ -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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-event-intelligence",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
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",
|
|
@@ -54,7 +54,10 @@
|
|
|
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",
|
|
@@ -83,11 +83,37 @@ export type HostWakeHandler = (
|
|
|
83
83
|
activation?: ActivationEnvelope,
|
|
84
84
|
) => Promise<HostWakeReceipt | string> | HostWakeReceipt | string;
|
|
85
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
|
+
|
|
86
96
|
export interface EventIntelligenceStore {
|
|
87
97
|
init?(): Promise<unknown>;
|
|
98
|
+
storeCapabilities?(): EventIntelligenceStoreCapabilities;
|
|
88
99
|
/** Required when non-default scopes are used. Implementations should return an isolated store view. */
|
|
89
100
|
forScope?(scopeId: string): Promise<EventIntelligenceStore> | EventIntelligenceStore;
|
|
90
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>;
|
|
91
117
|
[key: string]: any;
|
|
92
118
|
}
|
|
93
119
|
|
|
@@ -114,25 +140,26 @@ export interface EventIntelligenceScopedHost {
|
|
|
114
140
|
readonly triggerInspector: any;
|
|
115
141
|
readonly triggerPlanner: any;
|
|
116
142
|
readonly activationHydrator: any;
|
|
117
|
-
readonly eventSources: any[];
|
|
143
|
+
readonly eventSources: Promise<any[]> | any[];
|
|
144
|
+
readonly storeCapabilities?: EventIntelligenceStoreCapabilities;
|
|
118
145
|
describeTriggerLanguage(input?: {
|
|
119
|
-
category?: 'predicates' | '
|
|
146
|
+
category?: 'predicates' | 'pattern' | 'windows' | 'aggregates' | 'state' | 'selection' | 'semantic' | 'execution' | 'timing' | 'lifecycle';
|
|
120
147
|
operator?: string;
|
|
121
148
|
}): Record<string, unknown>;
|
|
122
149
|
planTrigger(input: TriggerPlanInput): Promise<{
|
|
123
|
-
planVersion: '
|
|
150
|
+
planVersion: '2';
|
|
124
151
|
definition: any;
|
|
125
152
|
connectionIds: string[];
|
|
126
153
|
resolvedSources: any[];
|
|
127
154
|
warnings: any[];
|
|
128
155
|
explanation: any;
|
|
129
156
|
}>;
|
|
130
|
-
hydrateWake(wakeId: string): ActivationEnvelope;
|
|
157
|
+
hydrateWake(wakeId: string): Promise<ActivationEnvelope> | ActivationEnvelope;
|
|
131
158
|
attachMcpClient(connection: HostMcpConnectionOptions): Promise<{
|
|
132
159
|
connectionId: string;
|
|
133
160
|
events: any[];
|
|
134
161
|
}>;
|
|
135
|
-
mcpStatus(): any[];
|
|
162
|
+
mcpStatus(): Promise<any[]> | any[];
|
|
136
163
|
}
|
|
137
164
|
|
|
138
165
|
export interface EventIntelligenceHost extends EventIntelligenceScopedHost {
|
|
@@ -170,6 +197,22 @@ export function createEventIntelligenceHost(
|
|
|
170
197
|
): Promise<EventIntelligenceHost>;
|
|
171
198
|
|
|
172
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
|
+
|
|
173
216
|
export class PersistentEventStore implements EventIntelligenceStore {
|
|
174
217
|
constructor(dataDir: string);
|
|
175
218
|
readonly dataDir: string;
|
|
@@ -12,6 +12,11 @@ import {
|
|
|
12
12
|
PersistentEventStore,
|
|
13
13
|
normalizeEventScopeId,
|
|
14
14
|
} from './lib/persistent-event-store.mjs';
|
|
15
|
+
import {
|
|
16
|
+
assertSharedStoreCapabilities,
|
|
17
|
+
describeStoreCapabilities,
|
|
18
|
+
validateSharedStoreCapabilities,
|
|
19
|
+
} from './lib/store-capabilities.mjs';
|
|
15
20
|
|
|
16
21
|
function normalizeMcpConnection(input) {
|
|
17
22
|
const connectionId = String(
|
|
@@ -75,6 +80,7 @@ function scopedHostView(runtime, context) {
|
|
|
75
80
|
scopeId,
|
|
76
81
|
runtime: context,
|
|
77
82
|
store: context.store,
|
|
83
|
+
storeCapabilities: describeStoreCapabilities(context.store),
|
|
78
84
|
triggerControl: context.triggerControl,
|
|
79
85
|
triggerInspector: context.triggerInspector,
|
|
80
86
|
triggerPlanner: context.triggerPlanner,
|
|
@@ -98,8 +104,8 @@ function scopedHostView(runtime, context) {
|
|
|
98
104
|
});
|
|
99
105
|
return runtime.mcpEventsClient.attachConnection(normalized);
|
|
100
106
|
},
|
|
101
|
-
mcpStatus() {
|
|
102
|
-
return runtime.mcpEventsClient.status()
|
|
107
|
+
async mcpStatus() {
|
|
108
|
+
return (await runtime.mcpEventsClient.status())
|
|
103
109
|
.filter((entry) => entry.scopeId === scopeId);
|
|
104
110
|
},
|
|
105
111
|
};
|
|
@@ -231,6 +237,9 @@ export async function createEventIntelligenceHost({
|
|
|
231
237
|
get store() {
|
|
232
238
|
return runtime.store;
|
|
233
239
|
},
|
|
240
|
+
get storeCapabilities() {
|
|
241
|
+
return runtime.storeCapabilities;
|
|
242
|
+
},
|
|
234
243
|
get triggerControl() {
|
|
235
244
|
return runtime.triggerControl;
|
|
236
245
|
},
|
|
@@ -264,8 +273,8 @@ export async function createEventIntelligenceHost({
|
|
|
264
273
|
registryManaged.delete(connectionId);
|
|
265
274
|
return runtime.mcpEventsClient.detachConnection(connectionId);
|
|
266
275
|
},
|
|
267
|
-
mcpStatus() {
|
|
268
|
-
return runtime.mcpEventsClient.status();
|
|
276
|
+
async mcpStatus() {
|
|
277
|
+
return await runtime.mcpEventsClient.status();
|
|
269
278
|
},
|
|
270
279
|
loadedScopes() {
|
|
271
280
|
return runtime.loadedScopes();
|
|
@@ -283,6 +292,9 @@ export async function createEventIntelligenceHost({
|
|
|
283
292
|
}
|
|
284
293
|
|
|
285
294
|
export {
|
|
295
|
+
assertSharedStoreCapabilities,
|
|
286
296
|
createHostMcpEventsConnection,
|
|
297
|
+
describeStoreCapabilities,
|
|
287
298
|
PersistentEventStore,
|
|
299
|
+
validateSharedStoreCapabilities,
|
|
288
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}`,
|
|
@@ -106,7 +106,7 @@ export class ActivationHydrator {
|
|
|
106
106
|
}));
|
|
107
107
|
|
|
108
108
|
return parseActivationEnvelope({
|
|
109
|
-
activationVersion: '
|
|
109
|
+
activationVersion: '2',
|
|
110
110
|
wake: {
|
|
111
111
|
wakeId,
|
|
112
112
|
status: wake.status,
|
|
@@ -118,14 +118,14 @@ export class ActivationHydrator {
|
|
|
118
118
|
triggerId: definition.triggerId,
|
|
119
119
|
version: definition.version,
|
|
120
120
|
description: definition.description ?? null,
|
|
121
|
-
|
|
121
|
+
pattern: definition.pattern,
|
|
122
122
|
lifecycle: definition.lifecycle ?? {},
|
|
123
123
|
},
|
|
124
124
|
continuation,
|
|
125
125
|
match: {
|
|
126
126
|
matchId: match.matchId,
|
|
127
127
|
status: match.status,
|
|
128
|
-
|
|
128
|
+
partitionKey: match.partitionKey,
|
|
129
129
|
openedAt: match.openedAt,
|
|
130
130
|
updatedAt: match.updatedAt,
|
|
131
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
|
|
|
@@ -60,7 +58,7 @@ export class CompositeEventConsumer {
|
|
|
60
58
|
for (const result of results) {
|
|
61
59
|
if (!result.matched || !result.match) continue;
|
|
62
60
|
|
|
63
|
-
const definition = this.store.listTriggers().find(
|
|
61
|
+
const definition = (await this.store.listTriggers()).find(
|
|
64
62
|
(candidate) =>
|
|
65
63
|
candidate.triggerId === result.match.triggerId &&
|
|
66
64
|
candidate.version === result.match.triggerVersion,
|
|
@@ -139,31 +137,13 @@ export class CompositeEventConsumer {
|
|
|
139
137
|
const delivery = await coordinator.deliverMatched(result.match);
|
|
140
138
|
const persistedWake = delivery.wake;
|
|
141
139
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
persistedWake.runtimeReceiptId,
|
|
150
|
-
);
|
|
151
|
-
deliveries.push({
|
|
152
|
-
triggerId: result.triggerId,
|
|
153
|
-
matchId: result.match.matchId,
|
|
154
|
-
wakeId: handled.wakeId,
|
|
155
|
-
status: handled.status,
|
|
156
|
-
runtimeReceiptId: handled.runtimeReceiptId ?? null,
|
|
157
|
-
});
|
|
158
|
-
} else {
|
|
159
|
-
deliveries.push({
|
|
160
|
-
triggerId: result.triggerId,
|
|
161
|
-
matchId: result.match.matchId,
|
|
162
|
-
wakeId: persistedWake?.wakeId ?? null,
|
|
163
|
-
status: delivery.status,
|
|
164
|
-
runtimeReceiptId: persistedWake?.runtimeReceiptId ?? null,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
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
|
+
});
|
|
167
147
|
}
|
|
168
148
|
|
|
169
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 &&
|