@rocketlang/aegis-guard 0.1.0 → 0.2.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/README.md +86 -0
- package/package.json +2 -2
- package/src/acc-bus.ts +71 -0
- package/src/approval-token.ts +87 -51
- package/src/idempotency.ts +13 -1
- package/src/index.ts +11 -0
- package/src/sense.ts +20 -0
package/README.md
CHANGED
|
@@ -127,3 +127,89 @@ const redisNonceStore: NonceStore = {
|
|
|
127
127
|
## License
|
|
128
128
|
|
|
129
129
|
AGPL-3.0 — Capt. Anil Sharma, powerpbox.org
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## v0.2.0 — Opt-in Agentic Control Center (ACC) event bus
|
|
134
|
+
|
|
135
|
+
Added 2026-05-17. Each Five Locks primitive now emits an `AccReceipt` on
|
|
136
|
+
success or failure, **but only when you wire a bus**. Without `setEventBus`,
|
|
137
|
+
v0.2.0 behaves identically to v0.1.0 — no emission, no state, no side effect.
|
|
138
|
+
|
|
139
|
+
### Wire it in 3 lines
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import { setEventBus, type EventBus, type AccReceipt } from '@rocketlang/aegis-guard';
|
|
143
|
+
|
|
144
|
+
const myBus: EventBus = {
|
|
145
|
+
emit: (r: AccReceipt) => console.log(`[ACC] ${r.event_type} verdict=${r.verdict} ${r.summary}`),
|
|
146
|
+
};
|
|
147
|
+
setEventBus(myBus);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Now every primitive call emits a receipt. Pass `null` to `setEventBus` to detach.
|
|
151
|
+
|
|
152
|
+
### Receipt events emitted
|
|
153
|
+
|
|
154
|
+
| Primitive | event_type on success | event_type on failure |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| `verifyApprovalToken` | `lock.approval.verified` (PASS) | `lock.approval.rejected` (FAIL) |
|
|
157
|
+
| `verifyAndConsumeNonce` | `lock.nonce.consumed` (PASS) | `lock.nonce.rejected` (FAIL) |
|
|
158
|
+
| `checkIdempotency` | `lock.idempotency.duplicate` (PASS) OR `lock.idempotency.mismatch` (WARN) | (no event for non-duplicate path) |
|
|
159
|
+
| `emitAegisSenseEvent` | `lock.sense.emitted` (PASS or WARN if irreversible) | — |
|
|
160
|
+
|
|
161
|
+
### Receipt shape
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
interface AccReceipt {
|
|
165
|
+
receipt_id: string; // primitive-prefixed identifier
|
|
166
|
+
primitive: string; // always 'aegis-guard' for this package
|
|
167
|
+
event_type: string; // lock.*
|
|
168
|
+
emitted_at: string; // ISO 8601
|
|
169
|
+
agent_id?: string; // reserved — not yet populated by aegis-guard
|
|
170
|
+
verdict?: string; // PASS | FAIL | WARN
|
|
171
|
+
rules_fired?: string[]; // e.g. ['AEG-E-016']
|
|
172
|
+
summary?: string; // ≤200 chars
|
|
173
|
+
payload?: Record<string, unknown>;
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The shape is a strict subset of the EE PRAMANA receipt format. EE
|
|
178
|
+
consumers ingest these events without translation.
|
|
179
|
+
|
|
180
|
+
### Phase-1 limits (v0.2.0)
|
|
181
|
+
|
|
182
|
+
- **agent_id is not yet populated** — primitives don't receive an agent
|
|
183
|
+
context as parameter. Future versions may add an optional `agent_id`
|
|
184
|
+
argument to each primitive; today you can post-process receipts in the
|
|
185
|
+
bus to add agent context from your own tracking.
|
|
186
|
+
- **`buildIdempotencyFingerprint`, `digestApprovalToken`, `mintApprovalToken`
|
|
187
|
+
do NOT emit** — they're pure helpers called many times per operation.
|
|
188
|
+
Emitting from them would flood the bus.
|
|
189
|
+
- **`buildQualityMaskAtPromotion`, `buildQualityDriftScore`,
|
|
190
|
+
`meetsHgQualityRequirement` do NOT emit** — quality computation is
|
|
191
|
+
scoring, not a governance decision. They're called during promotion
|
|
192
|
+
decisions; the calling code emits the governance event.
|
|
193
|
+
- **Default bus is in-process only.** Multi-process buses (Redis-backed,
|
|
194
|
+
etc.) are a consumer choice — implement the `EventBus` interface and
|
|
195
|
+
call `setEventBus(yourBus)`.
|
|
196
|
+
|
|
197
|
+
### Use with `@rocketlang/aegis-suite`
|
|
198
|
+
|
|
199
|
+
If you installed the meta-package, you can wire all 6 primitives in one call:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { wireAllToBus } from '@rocketlang/aegis-suite'; // available in suite v0.2.0+
|
|
203
|
+
wireAllToBus(); // default: in-memory bus + SQLite writer to ~/.aegis/acc-events.db
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
This sets up the bus on aegis-guard + chitta-detect + lakshmanrekha + hanumang-mandate
|
|
207
|
+
all at once, and persists events for the Agentic Control Center page.
|
|
208
|
+
|
|
209
|
+
### Discipline
|
|
210
|
+
|
|
211
|
+
- **Stateless contract preserved.** Primitives hold no state beyond a
|
|
212
|
+
module-private bus reference. Pass `null` to `setEventBus` to detach.
|
|
213
|
+
- **Emission must never throw.** If your bus implementation throws,
|
|
214
|
+
the primitive's caller is unaffected — the receipt is silently dropped.
|
|
215
|
+
This is intentional; observability must not break the governed path.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rocketlang/aegis-guard",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "AEGIS Guard SDK —
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "AEGIS Guard SDK — Five Locks primitives (approval-token, nonce, idempotency, SENSE, quality-evidence) + opt-in Agentic Control Center event bus for AEGIS-governed services",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": "Capt. Anil Sharma <capt.anil.sharma@powerpbox.org>",
|
package/src/acc-bus.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 Capt. Anil Sharma (rocketlang). All rights reserved.
|
|
3
|
+
// See LICENSE for details.
|
|
4
|
+
//
|
|
5
|
+
// @rocketlang/aegis-guard — opt-in ACC event bus integration (v0.2.0)
|
|
6
|
+
// @rule:ACC-003 — Opt-in event bus. emit only when setEventBus() called.
|
|
7
|
+
// @rule:ACC-004 — Lightweight OSS receipt shape (strict subset of EE PRAMANA).
|
|
8
|
+
// @rule:ACC-YK-003 — Stateless-primitive contract preserved. No bus = no emit.
|
|
9
|
+
// @rule:INF-ACC-005 — emit() is a no-op when no bus has been set.
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Lightweight receipt shape — structurally compatible with the canonical
|
|
13
|
+
* AccReceipt in /root/aegis/src/acc/types.ts. Defined locally so
|
|
14
|
+
* this primitive can ship without depending on the ACC package.
|
|
15
|
+
*/
|
|
16
|
+
export interface AccReceipt {
|
|
17
|
+
receipt_id: string;
|
|
18
|
+
primitive: string;
|
|
19
|
+
event_type: string;
|
|
20
|
+
emitted_at: string;
|
|
21
|
+
agent_id?: string;
|
|
22
|
+
verdict?: string;
|
|
23
|
+
rules_fired?: string[];
|
|
24
|
+
summary?: string;
|
|
25
|
+
payload?: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface EventBus {
|
|
29
|
+
emit(receipt: AccReceipt): void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Module-private bus reference. null by default — emission is no-op.
|
|
33
|
+
let _bus: EventBus | null = null;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Opt-in: provide an event bus to receive lightweight ACC receipts
|
|
37
|
+
* for every Five Locks primitive call. Pass null to detach.
|
|
38
|
+
*
|
|
39
|
+
* Default behaviour (no bus set): primitives behave EXACTLY as v0.1.0 —
|
|
40
|
+
* no emission, no state, no side effect beyond their primary return value.
|
|
41
|
+
*
|
|
42
|
+
* @rule:ACC-003 @rule:ACC-YK-003
|
|
43
|
+
*/
|
|
44
|
+
export function setEventBus(bus: EventBus | null): void {
|
|
45
|
+
_bus = bus;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Internal helper — primitives call this to emit a receipt. No-op when
|
|
50
|
+
* no bus is set. MUST NOT throw — bus implementation handles delivery
|
|
51
|
+
* failures (ACC-YK-003 stateless contract).
|
|
52
|
+
*
|
|
53
|
+
* @rule:INF-ACC-005
|
|
54
|
+
*/
|
|
55
|
+
export function emitAccReceipt(receipt: Omit<AccReceipt, 'primitive' | 'emitted_at'>): void {
|
|
56
|
+
if (!_bus) return;
|
|
57
|
+
try {
|
|
58
|
+
_bus.emit({
|
|
59
|
+
...receipt,
|
|
60
|
+
primitive: 'aegis-guard',
|
|
61
|
+
emitted_at: new Date().toISOString(),
|
|
62
|
+
});
|
|
63
|
+
} catch {
|
|
64
|
+
// bus implementation failure must never break the primitive's caller
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Test/introspection helper — does the primitive have a bus set right now? */
|
|
69
|
+
export function isBusWired(): boolean {
|
|
70
|
+
return _bus !== null;
|
|
71
|
+
}
|
package/src/approval-token.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { createHash } from 'crypto';
|
|
6
6
|
import { IrrNoApprovalError } from './errors.js';
|
|
7
7
|
import { type NonceStore, defaultNonceStore } from './nonce.js';
|
|
8
|
+
import { emitAccReceipt } from './acc-bus.js';
|
|
8
9
|
|
|
9
10
|
// Token may arrive up to 60s before local clock (NTP tolerance).
|
|
10
11
|
const CLOCK_SKEW_MS = 60_000;
|
|
@@ -41,48 +42,63 @@ export function verifyApprovalToken(
|
|
|
41
42
|
expectedCapability: string,
|
|
42
43
|
expectedOperation: string,
|
|
43
44
|
): ApprovalTokenPayload {
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
// @rule:ACC-003 @rule:ACC-004 — emit ACC receipt on success OR failure
|
|
46
|
+
const scope = `${expectedServiceId}/${expectedCapability}/${expectedOperation}`;
|
|
46
47
|
try {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
throw new IrrNoApprovalError(
|
|
55
|
-
expectedCapability,
|
|
56
|
-
`AEG-E-016: token scoped to '${payload.service_id}', not '${expectedServiceId}'`,
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (payload.capability !== expectedCapability) {
|
|
61
|
-
throw new IrrNoApprovalError(
|
|
62
|
-
expectedCapability,
|
|
63
|
-
`AEG-E-016: token capability '${payload.capability}' does not match '${expectedCapability}'`,
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (payload.operation !== expectedOperation) {
|
|
68
|
-
throw new IrrNoApprovalError(
|
|
69
|
-
expectedCapability,
|
|
70
|
-
`AEG-E-016: token operation '${payload.operation}' does not match '${expectedOperation}'`,
|
|
71
|
-
);
|
|
72
|
-
}
|
|
48
|
+
let payload: ApprovalTokenPayload;
|
|
49
|
+
try {
|
|
50
|
+
const decoded = Buffer.from(token, 'base64url').toString('utf8');
|
|
51
|
+
payload = JSON.parse(decoded) as ApprovalTokenPayload;
|
|
52
|
+
} catch {
|
|
53
|
+
throw new IrrNoApprovalError(expectedCapability, 'token could not be decoded');
|
|
54
|
+
}
|
|
73
55
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
56
|
+
if (payload.service_id !== expectedServiceId) {
|
|
57
|
+
throw new IrrNoApprovalError(
|
|
58
|
+
expectedCapability,
|
|
59
|
+
`AEG-E-016: token scoped to '${payload.service_id}', not '${expectedServiceId}'`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if (payload.capability !== expectedCapability) {
|
|
63
|
+
throw new IrrNoApprovalError(
|
|
64
|
+
expectedCapability,
|
|
65
|
+
`AEG-E-016: token capability '${payload.capability}' does not match '${expectedCapability}'`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
if (payload.operation !== expectedOperation) {
|
|
69
|
+
throw new IrrNoApprovalError(
|
|
70
|
+
expectedCapability,
|
|
71
|
+
`AEG-E-016: token operation '${payload.operation}' does not match '${expectedOperation}'`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
if (Date.now() > payload.expires_at) {
|
|
75
|
+
throw new IrrNoApprovalError(expectedCapability, 'AEG-E-016: token expired');
|
|
76
|
+
}
|
|
77
|
+
if (payload.issued_at !== undefined && payload.issued_at > Date.now() + CLOCK_SKEW_MS) {
|
|
78
|
+
throw new IrrNoApprovalError(
|
|
79
|
+
expectedCapability,
|
|
80
|
+
'AEG-E-016: token issued_at is in the future (clock skew > 60s or forged timestamp)',
|
|
81
|
+
);
|
|
82
|
+
}
|
|
77
83
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
emitAccReceipt({
|
|
85
|
+
receipt_id: `aegis-guard-verify-${digestApprovalToken(token)}`,
|
|
86
|
+
event_type: 'lock.approval.verified',
|
|
87
|
+
verdict: 'PASS',
|
|
88
|
+
rules_fired: ['AEG-E-016'],
|
|
89
|
+
summary: scope,
|
|
90
|
+
});
|
|
91
|
+
return payload;
|
|
92
|
+
} catch (err) {
|
|
93
|
+
emitAccReceipt({
|
|
94
|
+
receipt_id: `aegis-guard-verify-fail-${Date.now()}`,
|
|
95
|
+
event_type: 'lock.approval.rejected',
|
|
96
|
+
verdict: 'FAIL',
|
|
97
|
+
rules_fired: ['AEG-E-016'],
|
|
98
|
+
summary: `${scope} — ${(err as Error).message?.slice(0, 160) ?? 'verification failed'}`,
|
|
99
|
+
});
|
|
100
|
+
throw err;
|
|
83
101
|
}
|
|
84
|
-
|
|
85
|
-
return payload;
|
|
86
102
|
}
|
|
87
103
|
|
|
88
104
|
// @rule:AEG-HG-2B-006 — consume nonce before any state mutation; missing nonce = hard reject.
|
|
@@ -91,19 +107,39 @@ export async function verifyAndConsumeNonce(
|
|
|
91
107
|
payload: ApprovalTokenPayload,
|
|
92
108
|
store: NonceStore = defaultNonceStore,
|
|
93
109
|
): Promise<void> {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
110
|
+
// @rule:ACC-003 @rule:ACC-004 — emit ACC receipt on success OR failure
|
|
111
|
+
const scope = `${payload.service_id}/${payload.capability}/${payload.operation}`;
|
|
112
|
+
try {
|
|
113
|
+
if (!payload.nonce) {
|
|
114
|
+
throw new IrrNoApprovalError(
|
|
115
|
+
payload.capability,
|
|
116
|
+
'AEG-E-016: irreversible operation requires nonce for replay prevention',
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const ttlMs = Math.max(0, payload.expires_at - Date.now());
|
|
120
|
+
const consumed = await store.consumeNonce(payload.nonce, ttlMs);
|
|
121
|
+
if (!consumed) {
|
|
122
|
+
throw new IrrNoApprovalError(
|
|
123
|
+
payload.capability,
|
|
124
|
+
`AEG-E-016: nonce '${payload.nonce}' already consumed — approval replay rejected`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
emitAccReceipt({
|
|
128
|
+
receipt_id: `aegis-guard-nonce-${payload.nonce}`,
|
|
129
|
+
event_type: 'lock.nonce.consumed',
|
|
130
|
+
verdict: 'PASS',
|
|
131
|
+
rules_fired: ['AEG-HG-2B-006'],
|
|
132
|
+
summary: scope,
|
|
133
|
+
});
|
|
134
|
+
} catch (err) {
|
|
135
|
+
emitAccReceipt({
|
|
136
|
+
receipt_id: `aegis-guard-nonce-fail-${Date.now()}`,
|
|
137
|
+
event_type: 'lock.nonce.rejected',
|
|
138
|
+
verdict: 'FAIL',
|
|
139
|
+
rules_fired: ['AEG-HG-2B-006'],
|
|
140
|
+
summary: `${scope} — ${(err as Error).message?.slice(0, 160) ?? 'nonce check failed'}`,
|
|
141
|
+
});
|
|
142
|
+
throw err;
|
|
107
143
|
}
|
|
108
144
|
}
|
|
109
145
|
|
package/src/idempotency.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// @rule:AEG-HG-2B-006 — idempotency protects the operation; nonce protects the approval (separate locks)
|
|
2
2
|
// Pattern: check DB for externalRef before mutating. Matching fingerprint = safe no-op. Mismatch = warn + reject.
|
|
3
3
|
|
|
4
|
+
import { emitAccReceipt } from './acc-bus.js';
|
|
5
|
+
|
|
4
6
|
export interface IdempotencyCheckResult {
|
|
5
7
|
isDuplicate: boolean;
|
|
6
8
|
payloadMismatch: boolean;
|
|
@@ -22,11 +24,21 @@ export function checkIdempotency(
|
|
|
22
24
|
}
|
|
23
25
|
const payloadMismatch =
|
|
24
26
|
existingFingerprint !== undefined && existingFingerprint !== newFingerprint;
|
|
25
|
-
|
|
27
|
+
const result: IdempotencyCheckResult = {
|
|
26
28
|
isDuplicate: true,
|
|
27
29
|
payloadMismatch,
|
|
28
30
|
safeNoOp: !payloadMismatch,
|
|
29
31
|
};
|
|
32
|
+
emitAccReceipt({
|
|
33
|
+
receipt_id: `aegis-guard-idem-${_externalRef}`,
|
|
34
|
+
event_type: payloadMismatch ? 'lock.idempotency.mismatch' : 'lock.idempotency.duplicate',
|
|
35
|
+
verdict: payloadMismatch ? 'WARN' : 'PASS',
|
|
36
|
+
rules_fired: ['AEG-HG-2B-006'],
|
|
37
|
+
summary: payloadMismatch
|
|
38
|
+
? `duplicate externalRef ${_externalRef} with payload mismatch — caller must reject or escalate`
|
|
39
|
+
: `duplicate externalRef ${_externalRef} — safe no-op, return existing`,
|
|
40
|
+
});
|
|
41
|
+
return result;
|
|
30
42
|
}
|
|
31
43
|
|
|
32
44
|
// Build a stable base64 fingerprint from an arbitrary operation payload.
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
// @rocketlang/aegis-guard — AEGIS Guard SDK public API
|
|
2
2
|
// Five Locks proved in carbonx-backend (batches 62-74). Batch 93 makes them reusable.
|
|
3
|
+
// v0.2.0 adds opt-in Agentic Control Center (ACC) event bus integration.
|
|
3
4
|
|
|
4
5
|
export { IrrNoApprovalError, AegisNonceError } from './errors.js';
|
|
5
6
|
|
|
7
|
+
// @rule:ACC-003 — Opt-in event bus for Agentic Control Center observability.
|
|
8
|
+
// Stateless contract preserved (ACC-YK-003): emit is no-op
|
|
9
|
+
// when setEventBus has not been called. v0.2.0+.
|
|
10
|
+
export {
|
|
11
|
+
type AccReceipt,
|
|
12
|
+
type EventBus,
|
|
13
|
+
setEventBus,
|
|
14
|
+
isBusWired,
|
|
15
|
+
} from './acc-bus.js';
|
|
16
|
+
|
|
6
17
|
export { type NonceStore, defaultNonceStore } from './nonce.js';
|
|
7
18
|
|
|
8
19
|
export {
|
package/src/sense.ts
CHANGED
|
@@ -33,6 +33,26 @@ export function configureSenseTransport(transport: SenseTransport): void {
|
|
|
33
33
|
|
|
34
34
|
// @rule:CA-003 — all three snapshot fields are required by the type; callers must supply them.
|
|
35
35
|
// approval_token_ref, if present, must already be the output of digestApprovalToken (AEG-HG-2B-005).
|
|
36
|
+
// @rule:ACC-003 — also emit an ACC receipt for cockpit observability (no-op when bus unset).
|
|
36
37
|
export function emitAegisSenseEvent(event: AegisSenseEvent): void {
|
|
37
38
|
_transport(event);
|
|
39
|
+
emitAccReceiptFromSense(event);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
import { emitAccReceipt } from './acc-bus.js';
|
|
43
|
+
|
|
44
|
+
function emitAccReceiptFromSense(event: AegisSenseEvent): void {
|
|
45
|
+
emitAccReceipt({
|
|
46
|
+
receipt_id: `aegis-guard-sense-${event.correlation_id || Date.now()}`,
|
|
47
|
+
event_type: 'lock.sense.emitted',
|
|
48
|
+
verdict: event.irreversible ? 'WARN' : 'PASS',
|
|
49
|
+
rules_fired: ['CA-003', 'AEG-HG-2B-003', 'AEG-HG-2B-005'],
|
|
50
|
+
summary: `${event.service_id}/${event.capability}/${event.operation} ${event.irreversible ? '(irreversible)' : ''}`,
|
|
51
|
+
payload: {
|
|
52
|
+
event_type: event.event_type,
|
|
53
|
+
correlation_id: event.correlation_id,
|
|
54
|
+
approval_token_ref: event.approval_token_ref,
|
|
55
|
+
delta: event.delta,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
38
58
|
}
|