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
|
@@ -163,23 +163,24 @@ export class TriggerControlPlane {
|
|
|
163
163
|
});
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
listEventSources({ connectionIds } = {}) {
|
|
166
|
+
async listEventSources({ connectionIds } = {}) {
|
|
167
167
|
return this.store.listEventSources({
|
|
168
168
|
connectionIds,
|
|
169
169
|
enabledOnly: true,
|
|
170
170
|
});
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
listTriggers({ owner: ownerInput } = {}) {
|
|
173
|
+
async listTriggers({ owner: ownerInput } = {}) {
|
|
174
174
|
const owner = ownerInput ? actorIdentity(ownerInput) : null;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
175
|
+
const definitions = await this.store.listTriggers();
|
|
176
|
+
const entries = await Promise.all(definitions.map(async (definition) => ({
|
|
177
|
+
definition,
|
|
178
|
+
state: await this.store.getTriggerState(
|
|
179
|
+
definition.triggerId,
|
|
180
|
+
definition.version,
|
|
181
|
+
),
|
|
182
|
+
})));
|
|
183
|
+
return entries
|
|
183
184
|
.filter((entry) => !owner || samePrincipal(entry.state.owner, owner));
|
|
184
185
|
}
|
|
185
186
|
|
|
@@ -236,9 +237,9 @@ export class TriggerControlPlane {
|
|
|
236
237
|
throw error;
|
|
237
238
|
}
|
|
238
239
|
|
|
239
|
-
this.assertSourcesAvailable(definition, allowedConnectionIds);
|
|
240
|
+
await this.assertSourcesAvailable(definition, allowedConnectionIds);
|
|
240
241
|
|
|
241
|
-
if (this.store.listTriggers().some((candidate) =>
|
|
242
|
+
if ((await this.store.listTriggers()).some((candidate) =>
|
|
242
243
|
candidate.triggerId === definition.triggerId &&
|
|
243
244
|
candidate.version === definition.version
|
|
244
245
|
)) {
|
|
@@ -321,7 +322,7 @@ export class TriggerControlPlane {
|
|
|
321
322
|
throw error;
|
|
322
323
|
}
|
|
323
324
|
|
|
324
|
-
const current = this.store.listTriggers().find(
|
|
325
|
+
const current = (await this.store.listTriggers()).find(
|
|
325
326
|
(candidate) =>
|
|
326
327
|
candidate.triggerId === triggerId &&
|
|
327
328
|
candidate.version === expectedVersion,
|
|
@@ -334,7 +335,7 @@ export class TriggerControlPlane {
|
|
|
334
335
|
throw error;
|
|
335
336
|
}
|
|
336
337
|
|
|
337
|
-
const currentState = this.store.getTriggerState(
|
|
338
|
+
const currentState = await this.store.getTriggerState(
|
|
338
339
|
triggerId,
|
|
339
340
|
expectedVersion,
|
|
340
341
|
);
|
|
@@ -351,16 +352,19 @@ export class TriggerControlPlane {
|
|
|
351
352
|
throw error;
|
|
352
353
|
}
|
|
353
354
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
.
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
355
|
+
let competing = null;
|
|
356
|
+
for (const candidate of (await this.store.listTriggers())
|
|
357
|
+
.filter((item) => item.triggerId === triggerId)) {
|
|
358
|
+
if (candidate.version === expectedVersion) continue;
|
|
359
|
+
const state = await this.store.getTriggerState(
|
|
360
|
+
candidate.triggerId,
|
|
361
|
+
candidate.version,
|
|
362
|
+
);
|
|
363
|
+
if (state.status === 'active' || state.status === 'paused') {
|
|
364
|
+
competing = candidate;
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
364
368
|
if (competing) {
|
|
365
369
|
const error = new Error(
|
|
366
370
|
`Another trigger version is current: ${triggerId}@${competing.version}`,
|
|
@@ -380,7 +384,7 @@ export class TriggerControlPlane {
|
|
|
380
384
|
error.code = 'TRIGGER_NEW_VERSION_REQUIRED';
|
|
381
385
|
throw error;
|
|
382
386
|
}
|
|
383
|
-
if (this.store.listTriggers().some((candidate) =>
|
|
387
|
+
if ((await this.store.listTriggers()).some((candidate) =>
|
|
384
388
|
candidate.triggerId === triggerId &&
|
|
385
389
|
candidate.version === definition.version
|
|
386
390
|
)) {
|
|
@@ -395,7 +399,7 @@ export class TriggerControlPlane {
|
|
|
395
399
|
? [...new Set(connectionIds.map(String).filter(Boolean))]
|
|
396
400
|
: currentState.connectionIds;
|
|
397
401
|
|
|
398
|
-
this.assertSourcesAvailable(definition, allowedConnectionIds);
|
|
402
|
+
await this.assertSourcesAvailable(definition, allowedConnectionIds);
|
|
399
403
|
|
|
400
404
|
await this.retireMatches(
|
|
401
405
|
triggerId,
|
|
@@ -486,7 +490,7 @@ export class TriggerControlPlane {
|
|
|
486
490
|
throw error;
|
|
487
491
|
}
|
|
488
492
|
|
|
489
|
-
const definition = this.store.listTriggers().find(
|
|
493
|
+
const definition = (await this.store.listTriggers()).find(
|
|
490
494
|
(candidate) =>
|
|
491
495
|
candidate.triggerId === triggerId &&
|
|
492
496
|
candidate.version === version,
|
|
@@ -497,7 +501,7 @@ export class TriggerControlPlane {
|
|
|
497
501
|
throw error;
|
|
498
502
|
}
|
|
499
503
|
|
|
500
|
-
const currentState = this.store.getTriggerState(triggerId, version);
|
|
504
|
+
const currentState = await this.store.getTriggerState(triggerId, version);
|
|
501
505
|
if (!samePrincipal(currentState.owner, owner)) {
|
|
502
506
|
const error = new Error('Trigger owner mismatch');
|
|
503
507
|
error.code = 'TRIGGER_OWNER_MISMATCH';
|
|
@@ -606,7 +610,7 @@ export class TriggerControlPlane {
|
|
|
606
610
|
throw error;
|
|
607
611
|
}
|
|
608
612
|
|
|
609
|
-
const definition = this.store.listTriggers().find(
|
|
613
|
+
const definition = (await this.store.listTriggers()).find(
|
|
610
614
|
(candidate) =>
|
|
611
615
|
candidate.triggerId === triggerId &&
|
|
612
616
|
candidate.version === version,
|
|
@@ -617,7 +621,7 @@ export class TriggerControlPlane {
|
|
|
617
621
|
throw error;
|
|
618
622
|
}
|
|
619
623
|
|
|
620
|
-
const currentState = this.store.getTriggerState(triggerId, version);
|
|
624
|
+
const currentState = await this.store.getTriggerState(triggerId, version);
|
|
621
625
|
if (!samePrincipal(currentState.owner, owner)) {
|
|
622
626
|
const error = new Error('Trigger owner mismatch');
|
|
623
627
|
error.code = 'TRIGGER_OWNER_MISMATCH';
|
|
@@ -673,7 +677,7 @@ export class TriggerControlPlane {
|
|
|
673
677
|
};
|
|
674
678
|
}
|
|
675
679
|
|
|
676
|
-
assertSourcesAvailable(definition, connectionIds) {
|
|
680
|
+
async assertSourcesAvailable(definition, connectionIds) {
|
|
677
681
|
const allowedConnectionIds = Array.isArray(connectionIds)
|
|
678
682
|
? [...new Set(connectionIds.map(String).filter(Boolean))]
|
|
679
683
|
: [];
|
|
@@ -685,7 +689,7 @@ export class TriggerControlPlane {
|
|
|
685
689
|
throw error;
|
|
686
690
|
}
|
|
687
691
|
|
|
688
|
-
const sources = this.listEventSources({
|
|
692
|
+
const sources = await this.listEventSources({
|
|
689
693
|
connectionIds: allowedConnectionIds,
|
|
690
694
|
});
|
|
691
695
|
|
|
@@ -771,19 +775,8 @@ export class TriggerControlPlane {
|
|
|
771
775
|
}
|
|
772
776
|
}
|
|
773
777
|
|
|
774
|
-
for (const field of definition.correlation?.deterministic?.fields ?? []) {
|
|
775
|
-
const clause = definition.clauses.find(
|
|
776
|
-
(candidate) => candidate.id === field.ref,
|
|
777
|
-
);
|
|
778
|
-
const source = clause ? resolveSource(clause) : null;
|
|
779
|
-
assertAdvertisedPath(
|
|
780
|
-
source,
|
|
781
|
-
field.path,
|
|
782
|
-
`Correlation ${field.ref}`,
|
|
783
|
-
);
|
|
784
|
-
}
|
|
785
778
|
|
|
786
|
-
|
|
779
|
+
{
|
|
787
780
|
const clauseForRef = (ref) =>
|
|
788
781
|
definition.clauses.find((candidate) => candidate.id === ref);
|
|
789
782
|
const sourceForRef = (ref) => {
|
|
@@ -791,7 +784,7 @@ export class TriggerControlPlane {
|
|
|
791
784
|
return clause ? resolveSource(clause) : null;
|
|
792
785
|
};
|
|
793
786
|
|
|
794
|
-
for (const dimension of definition.
|
|
787
|
+
for (const dimension of definition.pattern.partitionBy ?? []) {
|
|
795
788
|
for (const field of dimension.fields ?? []) {
|
|
796
789
|
assertAdvertisedPath(
|
|
797
790
|
sourceForRef(field.ref),
|
|
@@ -801,7 +794,7 @@ export class TriggerControlPlane {
|
|
|
801
794
|
}
|
|
802
795
|
}
|
|
803
796
|
|
|
804
|
-
walkPatternNode(definition.
|
|
797
|
+
walkPatternNode(definition.pattern.root, (ref, fieldPath, purpose) => {
|
|
805
798
|
assertAdvertisedPath(
|
|
806
799
|
sourceForRef(ref),
|
|
807
800
|
fieldPath,
|
|
@@ -869,7 +862,7 @@ export class TriggerControlPlane {
|
|
|
869
862
|
return allowedConnectionIds;
|
|
870
863
|
}
|
|
871
864
|
|
|
872
|
-
buildDerivedOutputPayloadSchema(definition) {
|
|
865
|
+
async buildDerivedOutputPayloadSchema(definition) {
|
|
873
866
|
if (!definition.derivedEvent) return null;
|
|
874
867
|
|
|
875
868
|
const properties = {
|
|
@@ -879,7 +872,7 @@ export class TriggerControlPlane {
|
|
|
879
872
|
triggerId: { type: 'string' },
|
|
880
873
|
triggerVersion: { type: 'string' },
|
|
881
874
|
matchId: { type: 'string' },
|
|
882
|
-
|
|
875
|
+
partitionKey: { type: ['string', 'null'] },
|
|
883
876
|
contractVersion: { type: 'string' },
|
|
884
877
|
schemaFingerprint: { type: 'string' },
|
|
885
878
|
},
|
|
@@ -899,7 +892,7 @@ export class TriggerControlPlane {
|
|
|
899
892
|
properties[key] = scalarSchemaForConstant(value);
|
|
900
893
|
}
|
|
901
894
|
|
|
902
|
-
const allSources = this.store.listEventSources();
|
|
895
|
+
const allSources = await this.store.listEventSources();
|
|
903
896
|
const sourceSchemaForRefPath = (ref, fieldPath) => {
|
|
904
897
|
const clause = definition.clauses.find(
|
|
905
898
|
(candidate) => candidate.id === ref,
|
|
@@ -992,7 +985,7 @@ export class TriggerControlPlane {
|
|
|
992
985
|
|
|
993
986
|
async assertDerivedOutputContract(definition) {
|
|
994
987
|
if (!definition.derivedEvent) return null;
|
|
995
|
-
const payloadSchema = this.buildDerivedOutputPayloadSchema(definition);
|
|
988
|
+
const payloadSchema = await this.buildDerivedOutputPayloadSchema(definition);
|
|
996
989
|
try {
|
|
997
990
|
return await this.contractRegistry.assertCompatible({
|
|
998
991
|
eventName: definition.derivedEvent.name,
|
|
@@ -1023,7 +1016,7 @@ export class TriggerControlPlane {
|
|
|
1023
1016
|
if (!definition.derivedEvent) return null;
|
|
1024
1017
|
|
|
1025
1018
|
const contractVersion = String(definition.derivedEvent.contractVersion);
|
|
1026
|
-
const payloadSchema = this.buildDerivedOutputPayloadSchema(definition);
|
|
1019
|
+
const payloadSchema = await this.buildDerivedOutputPayloadSchema(definition);
|
|
1027
1020
|
|
|
1028
1021
|
const contract = await this.contractRegistry.registerProducer({
|
|
1029
1022
|
eventName: definition.derivedEvent.name,
|
|
@@ -1066,7 +1059,7 @@ export class TriggerControlPlane {
|
|
|
1066
1059
|
|
|
1067
1060
|
async retireMatches(triggerId, version, reason) {
|
|
1068
1061
|
const now = this.now().toISOString();
|
|
1069
|
-
const matches = this.store.listTriggerMatches(triggerId)
|
|
1062
|
+
const matches = (await this.store.listTriggerMatches(triggerId))
|
|
1070
1063
|
.filter((record) =>
|
|
1071
1064
|
record.triggerVersion === version &&
|
|
1072
1065
|
(record.status === 'partial' || record.status === 'matched')
|
|
@@ -1078,10 +1071,12 @@ export class TriggerControlPlane {
|
|
|
1078
1071
|
status: 'expired',
|
|
1079
1072
|
updatedAt: now,
|
|
1080
1073
|
});
|
|
1081
|
-
for (const deadline of this.store.listTemporalDeadlines
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1074
|
+
for (const deadline of this.store.listTemporalDeadlines
|
|
1075
|
+
? await this.store.listTemporalDeadlines({
|
|
1076
|
+
matchId: record.matchId,
|
|
1077
|
+
status: 'pending',
|
|
1078
|
+
})
|
|
1079
|
+
: []) {
|
|
1085
1080
|
await this.store.setTemporalDeadlineStatus?.(
|
|
1086
1081
|
deadline.deadlineId,
|
|
1087
1082
|
'cancelled',
|
|
@@ -1106,7 +1101,7 @@ export class TriggerControlPlane {
|
|
|
1106
1101
|
await this.store.appendAudit({
|
|
1107
1102
|
auditId: await stableId(
|
|
1108
1103
|
'audit',
|
|
1109
|
-
`${kind}:${entityType}:${entityId}:${timestamp}:${this.store.auditLength()}`,
|
|
1104
|
+
`${kind}:${entityType}:${entityId}:${timestamp}:${await this.store.auditLength()}`,
|
|
1110
1105
|
),
|
|
1111
1106
|
traceId,
|
|
1112
1107
|
timestamp,
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CompositeTriggerEngine,
|
|
3
3
|
} from '../../dist/src/composite/engine.js';
|
|
4
|
-
import {
|
|
5
|
-
evaluateTemporalConditions,
|
|
6
|
-
} from '../../dist/src/composite/temporal.js';
|
|
7
4
|
import {
|
|
8
5
|
parseCompositeTriggerDefinition,
|
|
9
6
|
parseCorrelatableEvent,
|
|
@@ -11,8 +8,19 @@ import {
|
|
|
11
8
|
} from '../../dist/src/intelligenceProtocol/index.js';
|
|
12
9
|
|
|
13
10
|
function latest(records) {
|
|
11
|
+
const rank = (record) => {
|
|
12
|
+
if (record.status === 'fired') return 5;
|
|
13
|
+
if (record.status === 'emitted') return 4;
|
|
14
|
+
if (record.status === 'matched') return 3;
|
|
15
|
+
if (record.status === 'partial' && record.patternState?.role === 'match') return 2;
|
|
16
|
+
if (record.status === 'partial') return 1;
|
|
17
|
+
return 0;
|
|
18
|
+
};
|
|
19
|
+
|
|
14
20
|
return [...records].sort(
|
|
15
|
-
(a, b) =>
|
|
21
|
+
(a, b) =>
|
|
22
|
+
Date.parse(b.updatedAt) - Date.parse(a.updatedAt) ||
|
|
23
|
+
rank(b) - rank(a),
|
|
16
24
|
)[0] ?? null;
|
|
17
25
|
}
|
|
18
26
|
|
|
@@ -32,12 +40,6 @@ function sourceRef(event) {
|
|
|
32
40
|
};
|
|
33
41
|
}
|
|
34
42
|
|
|
35
|
-
function baseExpressionRefs(expression) {
|
|
36
|
-
return expression.kind === 'count'
|
|
37
|
-
? [expression.ref]
|
|
38
|
-
: expression.refs;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
43
|
function clauseState(definition, match) {
|
|
42
44
|
const seen = new Map();
|
|
43
45
|
for (const event of match?.sourceEvents ?? []) {
|
|
@@ -48,63 +50,19 @@ function clauseState(definition, match) {
|
|
|
48
50
|
|
|
49
51
|
return definition.clauses.map((clause) => {
|
|
50
52
|
const evidence = seen.get(clause.id) ?? [];
|
|
51
|
-
const requiredCount =
|
|
52
|
-
definition.expression.kind === 'count' &&
|
|
53
|
-
definition.expression.ref === clause.id
|
|
54
|
-
? definition.expression.atLeast
|
|
55
|
-
: 1;
|
|
56
53
|
return {
|
|
57
54
|
clauseId: clause.id,
|
|
58
55
|
event: clause.event,
|
|
59
56
|
serverId: clause.serverId ?? null,
|
|
60
57
|
contractVersion: clause.contractVersion ?? null,
|
|
61
|
-
status: evidence.length
|
|
58
|
+
status: evidence.length ? 'observed' : 'waiting',
|
|
62
59
|
observedCount: evidence.length,
|
|
63
|
-
requiredCount,
|
|
64
60
|
evidence,
|
|
65
61
|
};
|
|
66
62
|
});
|
|
67
63
|
}
|
|
68
64
|
|
|
69
|
-
function
|
|
70
|
-
const byId = new Map(clauses.map((entry) => [entry.clauseId, entry]));
|
|
71
|
-
const expression = definition.expression;
|
|
72
|
-
if (expression.kind === 'allOf') {
|
|
73
|
-
return expression.refs.every(
|
|
74
|
-
(ref) => byId.get(ref)?.status === 'satisfied',
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
if (expression.kind === 'anyOf') {
|
|
78
|
-
return expression.refs.some(
|
|
79
|
-
(ref) => byId.get(ref)?.status === 'satisfied',
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
if (expression.kind === 'count') {
|
|
83
|
-
return (byId.get(expression.ref)?.observedCount ?? 0) >= expression.atLeast;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const ordered = [];
|
|
87
|
-
for (const ref of expression.refs) {
|
|
88
|
-
const evidence = byId.get(ref)?.evidence ?? [];
|
|
89
|
-
if (!evidence.length) return false;
|
|
90
|
-
ordered.push(evidence[0]);
|
|
91
|
-
}
|
|
92
|
-
return ordered.every(
|
|
93
|
-
(event, index) =>
|
|
94
|
-
index === 0 ||
|
|
95
|
-
Date.parse(event.occurredAt) >
|
|
96
|
-
Date.parse(ordered[index - 1].occurredAt),
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function explain({
|
|
101
|
-
definition,
|
|
102
|
-
state,
|
|
103
|
-
match,
|
|
104
|
-
clauses,
|
|
105
|
-
temporal,
|
|
106
|
-
wake,
|
|
107
|
-
}) {
|
|
65
|
+
function explain({ state, match, wake }) {
|
|
108
66
|
if (state.status === 'paused') {
|
|
109
67
|
return {
|
|
110
68
|
code: 'trigger_paused',
|
|
@@ -132,8 +90,9 @@ function explain({
|
|
|
132
90
|
|
|
133
91
|
if (!match) {
|
|
134
92
|
return {
|
|
135
|
-
code: '
|
|
136
|
-
summary:
|
|
93
|
+
code: 'waiting_for_pattern_evidence',
|
|
94
|
+
summary: 'Waiting for evidence that can satisfy the Pattern AST.',
|
|
95
|
+
patternVersion: '2',
|
|
137
96
|
};
|
|
138
97
|
}
|
|
139
98
|
|
|
@@ -156,65 +115,22 @@ function explain({
|
|
|
156
115
|
if (match.status === 'matched') {
|
|
157
116
|
return {
|
|
158
117
|
code: 'matched_waiting_delivery',
|
|
159
|
-
summary: '
|
|
118
|
+
summary: 'The Pattern AST is satisfied; effect delivery is pending or not yet marked fired.',
|
|
160
119
|
};
|
|
161
120
|
}
|
|
162
121
|
if (match.status === 'expired') {
|
|
163
122
|
return {
|
|
164
123
|
code: 'match_expired',
|
|
165
|
-
summary: 'This match can no longer satisfy the trigger.',
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (definition.patternV2) {
|
|
170
|
-
return {
|
|
171
|
-
code: 'pattern_v2_waiting',
|
|
172
|
-
summary:
|
|
173
|
-
'Pattern AST v2 has buffered evidence but no new complete match is currently eligible.',
|
|
174
|
-
patternVersion: '2',
|
|
175
|
-
bufferRole: match.patternState?.role ?? null,
|
|
176
|
-
semanticDecisions:
|
|
177
|
-
match.patternState?.semanticDecisions ?? [],
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const missing = clauses
|
|
182
|
-
.filter((clause) => clause.status !== 'satisfied')
|
|
183
|
-
.map((clause) => clause.clauseId);
|
|
184
|
-
|
|
185
|
-
if (!baseExpressionSatisfied(definition, clauses)) {
|
|
186
|
-
return {
|
|
187
|
-
code: 'waiting_for_event_clauses',
|
|
188
|
-
summary: `Waiting for event clauses: ${missing.join(', ') || 'expression ordering'}.`,
|
|
189
|
-
missingClauses: missing,
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const blocked = temporal.conditionStates.filter(
|
|
194
|
-
(condition) => condition.status === 'blocked',
|
|
195
|
-
);
|
|
196
|
-
if (blocked.length) {
|
|
197
|
-
return {
|
|
198
|
-
code: 'temporal_blocked',
|
|
199
|
-
summary: `Temporal condition blocked: ${blocked.map((entry) => entry.conditionId).join(', ')}.`,
|
|
200
|
-
temporalConditionIds: blocked.map((entry) => entry.conditionId),
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const pending = temporal.conditionStates.filter(
|
|
205
|
-
(condition) => condition.status === 'pending',
|
|
206
|
-
);
|
|
207
|
-
if (pending.length) {
|
|
208
|
-
return {
|
|
209
|
-
code: 'waiting_for_temporal_conditions',
|
|
210
|
-
summary: `Waiting for temporal conditions: ${pending.map((entry) => entry.conditionId).join(', ')}.`,
|
|
211
|
-
temporalConditionIds: pending.map((entry) => entry.conditionId),
|
|
124
|
+
summary: 'This Pattern match can no longer satisfy the trigger.',
|
|
212
125
|
};
|
|
213
126
|
}
|
|
214
127
|
|
|
215
128
|
return {
|
|
216
|
-
code: '
|
|
217
|
-
summary: '
|
|
129
|
+
code: 'pattern_waiting',
|
|
130
|
+
summary: 'Pattern AST has buffered evidence but no new complete match is currently eligible.',
|
|
131
|
+
patternVersion: '2',
|
|
132
|
+
bufferRole: match.patternState?.role ?? null,
|
|
133
|
+
semanticDecisions: match.patternState?.semanticDecisions ?? [],
|
|
218
134
|
};
|
|
219
135
|
}
|
|
220
136
|
|
|
@@ -227,12 +143,12 @@ export class TriggerInspector {
|
|
|
227
143
|
this.now = now;
|
|
228
144
|
}
|
|
229
145
|
|
|
230
|
-
inspect({
|
|
146
|
+
async inspect({
|
|
231
147
|
triggerId,
|
|
232
148
|
version,
|
|
233
149
|
matchId,
|
|
234
150
|
}) {
|
|
235
|
-
const candidates = this.store.listTriggers()
|
|
151
|
+
const candidates = (await this.store.listTriggers())
|
|
236
152
|
.filter((definition) => definition.triggerId === triggerId)
|
|
237
153
|
.filter((definition) => !version || definition.version === version);
|
|
238
154
|
|
|
@@ -250,12 +166,12 @@ export class TriggerInspector {
|
|
|
250
166
|
})
|
|
251
167
|
)[0];
|
|
252
168
|
|
|
253
|
-
const state = this.store.getTriggerState(
|
|
169
|
+
const state = await this.store.getTriggerState(
|
|
254
170
|
definition.triggerId,
|
|
255
171
|
definition.version,
|
|
256
172
|
);
|
|
257
173
|
|
|
258
|
-
const matches = this.store.listTriggerMatches(definition.triggerId)
|
|
174
|
+
const matches = (await this.store.listTriggerMatches(definition.triggerId))
|
|
259
175
|
.filter((match) => match.triggerVersion === definition.version);
|
|
260
176
|
const match = matchId
|
|
261
177
|
? matches.find((candidate) => candidate.matchId === matchId) ?? null
|
|
@@ -268,23 +184,8 @@ export class TriggerInspector {
|
|
|
268
184
|
}
|
|
269
185
|
|
|
270
186
|
const clauses = clauseState(definition, match);
|
|
271
|
-
const
|
|
272
|
-
?
|
|
273
|
-
: {
|
|
274
|
-
outcome: definition.temporal?.length ? 'pending' : 'satisfied',
|
|
275
|
-
pendingDeadlines: [],
|
|
276
|
-
conditionStates: (definition.temporal ?? []).map((condition) => ({
|
|
277
|
-
conditionId: condition.id,
|
|
278
|
-
kind: condition.kind,
|
|
279
|
-
status: 'pending',
|
|
280
|
-
reason: 'waiting for a match',
|
|
281
|
-
})),
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const deadlines = match
|
|
285
|
-
? this.store.listTemporalDeadlines?.({
|
|
286
|
-
matchId: match.matchId,
|
|
287
|
-
}) ?? []
|
|
187
|
+
const deadlines = match && this.store.listTemporalDeadlines
|
|
188
|
+
? await this.store.listTemporalDeadlines({ matchId: match.matchId })
|
|
288
189
|
: [];
|
|
289
190
|
const pendingDeadlines = deadlines.filter(
|
|
290
191
|
(deadline) => deadline.status === 'pending',
|
|
@@ -294,25 +195,25 @@ export class TriggerInspector {
|
|
|
294
195
|
.sort()[0] ?? null;
|
|
295
196
|
|
|
296
197
|
const wake = match?.firedWakeId
|
|
297
|
-
? this.store.latestWake(match.firedWakeId)
|
|
198
|
+
? await this.store.latestWake(match.firedWakeId)
|
|
298
199
|
: null;
|
|
299
200
|
|
|
300
201
|
const derivedOutputs = match
|
|
301
|
-
? (this.store.listDerivedEvents?.({ matchId: match.matchId }) ?? [])
|
|
202
|
+
? (await this.store.listDerivedEvents?.({ matchId: match.matchId }) ?? [])
|
|
302
203
|
: [];
|
|
303
204
|
|
|
304
|
-
const
|
|
305
|
-
?
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
205
|
+
const rawHistory = match && this.store.listTriggerMatchHistory
|
|
206
|
+
? await this.store.listTriggerMatchHistory(match.matchId)
|
|
207
|
+
: (match ? [match] : []);
|
|
208
|
+
const matchHistory = rawHistory.map((record) => ({
|
|
209
|
+
status: record.status,
|
|
210
|
+
updatedAt: record.updatedAt,
|
|
211
|
+
firedWakeId: record.firedWakeId,
|
|
212
|
+
derivedEventIds: record.derivedEventIds ?? [],
|
|
213
|
+
sourceEventIds: record.sourceEvents.map(
|
|
214
|
+
(event) => event.sourceEventId,
|
|
215
|
+
),
|
|
216
|
+
}));
|
|
316
217
|
|
|
317
218
|
return {
|
|
318
219
|
trigger: {
|
|
@@ -322,9 +223,7 @@ export class TriggerInspector {
|
|
|
322
223
|
continuation: definition.continuation ?? null,
|
|
323
224
|
target: definition.target ?? null,
|
|
324
225
|
derivedEvent: definition.derivedEvent ?? null,
|
|
325
|
-
|
|
326
|
-
temporal: definition.temporal ?? [],
|
|
327
|
-
patternV2: definition.patternV2 ?? null,
|
|
226
|
+
pattern: definition.pattern,
|
|
328
227
|
lifecycle: definition.lifecycle ?? {},
|
|
329
228
|
},
|
|
330
229
|
lifecycle: state,
|
|
@@ -332,7 +231,7 @@ export class TriggerInspector {
|
|
|
332
231
|
? {
|
|
333
232
|
matchId: match.matchId,
|
|
334
233
|
status: match.status,
|
|
335
|
-
|
|
234
|
+
partitionKey: match.partitionKey,
|
|
336
235
|
openedAt: match.openedAt,
|
|
337
236
|
expiresAt: match.expiresAt,
|
|
338
237
|
updatedAt: match.updatedAt,
|
|
@@ -340,10 +239,6 @@ export class TriggerInspector {
|
|
|
340
239
|
}
|
|
341
240
|
: null,
|
|
342
241
|
clauses,
|
|
343
|
-
temporal: {
|
|
344
|
-
outcome: temporal.outcome,
|
|
345
|
-
conditions: temporal.conditionStates,
|
|
346
|
-
},
|
|
347
242
|
deadlines: deadlines.map((deadline) => ({
|
|
348
243
|
deadlineId: deadline.deadlineId,
|
|
349
244
|
conditionId: deadline.conditionId,
|
|
@@ -376,14 +271,7 @@ export class TriggerInspector {
|
|
|
376
271
|
matchHistory,
|
|
377
272
|
patternState: match?.patternState ?? null,
|
|
378
273
|
},
|
|
379
|
-
why: explain({
|
|
380
|
-
definition,
|
|
381
|
-
state,
|
|
382
|
-
match,
|
|
383
|
-
clauses,
|
|
384
|
-
temporal,
|
|
385
|
-
wake,
|
|
386
|
-
}),
|
|
274
|
+
why: explain({ state, match, wake }),
|
|
387
275
|
};
|
|
388
276
|
}
|
|
389
277
|
}
|
|
@@ -638,7 +526,7 @@ export async function simulateTrigger({
|
|
|
638
526
|
order,
|
|
639
527
|
evaluatedUntil: clock.toISOString(),
|
|
640
528
|
steps,
|
|
641
|
-
inspection: inspector.inspect({
|
|
529
|
+
inspection: await inspector.inspect({
|
|
642
530
|
triggerId: definition.triggerId,
|
|
643
531
|
version: definition.version,
|
|
644
532
|
}),
|