git-paid 0.0.0-stage → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/git-paid.js +211 -0
- package/brand/README.md +54 -0
- package/brand/postbuild-icon.png +0 -0
- package/brand/postbuild-icon.svg +14 -0
- package/brand/postbuild-logo.png +0 -0
- package/brand/postbuild-logo.svg +17 -0
- package/lib/auth.js +208 -0
- package/lib/directories.js +123 -0
- package/lib/engine.js +502 -0
- package/lib/fixer.js +168 -0
- package/lib/inbox.js +216 -0
- package/lib/mcp.js +907 -0
- package/lib/pseo.js +491 -0
- package/lib/stateful-mcp.js +485 -0
- package/lib/stripe.js +554 -0
- package/lib/submitter.js +189 -0
- package/package.json +42 -4
- package/server.js +252 -0
- package/README.md +0 -3
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postbuild Stateful MCP Middleware (v2.1.0)
|
|
3
|
+
*
|
|
4
|
+
* High-performance stateful middleware for Model Context Protocol (MCP) servers.
|
|
5
|
+
* Provides:
|
|
6
|
+
* 1. Watermark Context Residuals (`compactList` -> `unchanged@vN`, `updated@vN`, `rehydrated@vN`)
|
|
7
|
+
* 2. Sliding-Window Attention Rehydration (`maxConsecutiveDeltas`)
|
|
8
|
+
* 3. In-Memory Superset Query Cache (`queryWithSupersetCache` — 1-2ms subset filtering)
|
|
9
|
+
* 4. Two-Phase Cryptographic Write Guard (`evaluateWriteMutation` — HMAC-SHA256 OCC `ct_...` tokens)
|
|
10
|
+
* 5. Untrusted Content Spotlighting (`<<<UNTRUSTED_CONTENT_DATA_ONLY: ... >>>`)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import crypto from "node:crypto";
|
|
14
|
+
|
|
15
|
+
const DEFAULT_SECRET =
|
|
16
|
+
process.env.POSTBUILD_SECRET ||
|
|
17
|
+
"postbuild_mcp_stateful_occ_hmac_secret";
|
|
18
|
+
|
|
19
|
+
export function spotlightUntrustedText(value) {
|
|
20
|
+
if (value === null || value === undefined) return value;
|
|
21
|
+
const raw = String(value).trim();
|
|
22
|
+
if (!raw) return raw;
|
|
23
|
+
if (raw.startsWith("<<<UNTRUSTED_CONTENT_DATA_ONLY:")) return raw;
|
|
24
|
+
return `<<<UNTRUSTED_CONTENT_DATA_ONLY: ${raw} >>>`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Creates a generic entity adapter for stateful MCP tracking.
|
|
29
|
+
*/
|
|
30
|
+
export function createGenericEntityAdapter(config = {}) {
|
|
31
|
+
const {
|
|
32
|
+
name = "generic-entity",
|
|
33
|
+
idField = "id",
|
|
34
|
+
versionField = "version_etag",
|
|
35
|
+
updatedAtField = "updated_at",
|
|
36
|
+
stubFields = ["id", "name", "status"],
|
|
37
|
+
journalFields = [],
|
|
38
|
+
untrustedTextFields = [],
|
|
39
|
+
isActiveRecord = () => true
|
|
40
|
+
} = config;
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
name,
|
|
44
|
+
idField,
|
|
45
|
+
versionField,
|
|
46
|
+
updatedAtField,
|
|
47
|
+
stubFields,
|
|
48
|
+
journalFields,
|
|
49
|
+
untrustedTextFields,
|
|
50
|
+
isActiveRecord,
|
|
51
|
+
|
|
52
|
+
getId(record) {
|
|
53
|
+
return String(record?.[idField] ?? record?.id ?? "");
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
getWatermark(record) {
|
|
57
|
+
if (record?.[versionField] !== undefined && record?.[versionField] !== null) {
|
|
58
|
+
return String(record[versionField]);
|
|
59
|
+
}
|
|
60
|
+
if (record?.[updatedAtField]) {
|
|
61
|
+
return `v_${record[updatedAtField]}`;
|
|
62
|
+
}
|
|
63
|
+
const payload = JSON.stringify(record || {});
|
|
64
|
+
return "v1_" + crypto.createHash("sha1").update(payload).digest("hex").slice(0, 6);
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
projectInitial(record) {
|
|
68
|
+
if (!record || typeof record !== "object") return record;
|
|
69
|
+
const out = {};
|
|
70
|
+
for (const [k, v] of Object.entries(record)) {
|
|
71
|
+
if (journalFields.includes(k)) continue;
|
|
72
|
+
if (untrustedTextFields.includes(k) && typeof v === "string") {
|
|
73
|
+
out[k] = spotlightUntrustedText(v);
|
|
74
|
+
} else {
|
|
75
|
+
out[k] = v;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
projectStub(record, watermark) {
|
|
82
|
+
const stub = {};
|
|
83
|
+
stub[idField] = this.getId(record);
|
|
84
|
+
for (const f of stubFields) {
|
|
85
|
+
if (record[f] !== undefined) {
|
|
86
|
+
stub[f] = untrustedTextFields.includes(f)
|
|
87
|
+
? spotlightUntrustedText(record[f])
|
|
88
|
+
: record[f];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
stub._context_delta = `unchanged@${watermark}`;
|
|
92
|
+
return stub;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Stateful MCP Middleware instance factory.
|
|
99
|
+
*/
|
|
100
|
+
export function withStatefulMcp(options = {}) {
|
|
101
|
+
const {
|
|
102
|
+
adapter = createGenericEntityAdapter(),
|
|
103
|
+
maxConsecutiveDeltas = 8,
|
|
104
|
+
requireCommitTokenOnWrite = true,
|
|
105
|
+
commitTokenTtlMs = 5 * 60 * 1000
|
|
106
|
+
} = options;
|
|
107
|
+
|
|
108
|
+
const sessions = new Map();
|
|
109
|
+
const ticketVault = new Map();
|
|
110
|
+
|
|
111
|
+
const telemetry = {
|
|
112
|
+
totalCalls: 0,
|
|
113
|
+
supersetCacheHits: 0,
|
|
114
|
+
upstreamFetches: 0,
|
|
115
|
+
rawBytesEstimated: 0,
|
|
116
|
+
compactedBytesEmitted: 0,
|
|
117
|
+
deltasEmitted: 0,
|
|
118
|
+
rehydrationsEmitted: 0,
|
|
119
|
+
occDryRuns: 0,
|
|
120
|
+
occCommits: 0,
|
|
121
|
+
occRejections: 0
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
function getSession(sessionId = "global-default", principalId = "default") {
|
|
125
|
+
const key = `${sessionId}::${principalId}`;
|
|
126
|
+
if (!sessions.has(key)) {
|
|
127
|
+
sessions.set(key, {
|
|
128
|
+
entityMap: new Map(),
|
|
129
|
+
supersetCache: new Map(),
|
|
130
|
+
createdAt: Date.now(),
|
|
131
|
+
turns: 0
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return sessions.get(key);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function computeDiff(prevSnapshot = {}, nextSnapshot = {}) {
|
|
138
|
+
const changedFields = [];
|
|
139
|
+
const diffValues = {};
|
|
140
|
+
const allKeys = new Set([
|
|
141
|
+
...Object.keys(prevSnapshot || {}),
|
|
142
|
+
...Object.keys(nextSnapshot || {})
|
|
143
|
+
]);
|
|
144
|
+
for (const k of allKeys) {
|
|
145
|
+
if (k.startsWith("_")) continue;
|
|
146
|
+
if (adapter.journalFields.includes(k)) continue;
|
|
147
|
+
const a = JSON.stringify(prevSnapshot?.[k]);
|
|
148
|
+
const b = JSON.stringify(nextSnapshot?.[k]);
|
|
149
|
+
if (a !== b) {
|
|
150
|
+
changedFields.push(k);
|
|
151
|
+
diffValues[k] = adapter.untrustedTextFields.includes(k)
|
|
152
|
+
? spotlightUntrustedText(nextSnapshot?.[k])
|
|
153
|
+
: nextSnapshot?.[k];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { changedFields, diffValues };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
adapter,
|
|
161
|
+
telemetry,
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Evaluates a query against a warm in-memory superset cache before hitting upstream.
|
|
165
|
+
*/
|
|
166
|
+
async queryWithSupersetCache({
|
|
167
|
+
sessionId = "global-default",
|
|
168
|
+
principalId = "default",
|
|
169
|
+
cacheKey = "default_collection",
|
|
170
|
+
fetchLive,
|
|
171
|
+
filter = () => true,
|
|
172
|
+
limit = 50,
|
|
173
|
+
bypassCache = false,
|
|
174
|
+
ttlMs = 120 * 1000
|
|
175
|
+
}) {
|
|
176
|
+
const sess = getSession(sessionId, principalId);
|
|
177
|
+
const cached = sess.supersetCache.get(cacheKey);
|
|
178
|
+
const now = Date.now();
|
|
179
|
+
|
|
180
|
+
let baseRecords;
|
|
181
|
+
let cacheHit = false;
|
|
182
|
+
const t0 = performance.now();
|
|
183
|
+
|
|
184
|
+
if (!bypassCache && cached && now - cached.ts < ttlMs) {
|
|
185
|
+
baseRecords = cached.records;
|
|
186
|
+
cacheHit = true;
|
|
187
|
+
telemetry.supersetCacheHits += 1;
|
|
188
|
+
} else {
|
|
189
|
+
baseRecords = await fetchLive();
|
|
190
|
+
sess.supersetCache.set(cacheKey, {
|
|
191
|
+
records: Array.isArray(baseRecords) ? baseRecords : [],
|
|
192
|
+
ts: now
|
|
193
|
+
});
|
|
194
|
+
telemetry.upstreamFetches += 1;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const filtered = (Array.isArray(baseRecords) ? baseRecords : [])
|
|
198
|
+
.filter(filter)
|
|
199
|
+
.slice(0, limit);
|
|
200
|
+
|
|
201
|
+
const elapsedMs = Number((performance.now() - t0).toFixed(2));
|
|
202
|
+
return Object.assign(filtered, {
|
|
203
|
+
_mcp_cache_hit: cacheHit,
|
|
204
|
+
_mcp_query_ms: elapsedMs,
|
|
205
|
+
_mcp_superset_total: Array.isArray(baseRecords) ? baseRecords.length : 0
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Compacts a list of records using per-entity Watermark Context Residuals (`@vN`)
|
|
211
|
+
* and Sliding-Window Attention Rehydration (`maxConsecutiveDeltas`).
|
|
212
|
+
*/
|
|
213
|
+
compactList({
|
|
214
|
+
sessionId = "global-default",
|
|
215
|
+
principalId = "default",
|
|
216
|
+
records = [],
|
|
217
|
+
bypassCache = false
|
|
218
|
+
}) {
|
|
219
|
+
telemetry.totalCalls += 1;
|
|
220
|
+
const sess = getSession(sessionId, principalId);
|
|
221
|
+
sess.turns += 1;
|
|
222
|
+
|
|
223
|
+
const rawJson = JSON.stringify(records);
|
|
224
|
+
const rawBytes = Buffer.byteLength(rawJson, "utf8");
|
|
225
|
+
telemetry.rawBytesEstimated += rawBytes;
|
|
226
|
+
|
|
227
|
+
const compacted = [];
|
|
228
|
+
let unchangedCount = 0;
|
|
229
|
+
let updatedCount = 0;
|
|
230
|
+
let rehydratedCount = 0;
|
|
231
|
+
let initialCount = 0;
|
|
232
|
+
|
|
233
|
+
for (const rec of records) {
|
|
234
|
+
const id = adapter.getId(rec);
|
|
235
|
+
const watermark = adapter.getWatermark(rec);
|
|
236
|
+
const projected = adapter.projectInitial(rec);
|
|
237
|
+
const prev = sess.entityMap.get(id);
|
|
238
|
+
|
|
239
|
+
if (bypassCache || !prev) {
|
|
240
|
+
sess.entityMap.set(id, {
|
|
241
|
+
watermark,
|
|
242
|
+
snapshot: projected,
|
|
243
|
+
consecutiveDeltas: 0
|
|
244
|
+
});
|
|
245
|
+
compacted.push({
|
|
246
|
+
...projected,
|
|
247
|
+
_context_delta: `initial@${watermark}`
|
|
248
|
+
});
|
|
249
|
+
initialCount += 1;
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (prev.watermark === watermark) {
|
|
254
|
+
const nextConsecutive = prev.consecutiveDeltas + 1;
|
|
255
|
+
if (nextConsecutive >= maxConsecutiveDeltas) {
|
|
256
|
+
sess.entityMap.set(id, {
|
|
257
|
+
watermark,
|
|
258
|
+
snapshot: projected,
|
|
259
|
+
consecutiveDeltas: 0
|
|
260
|
+
});
|
|
261
|
+
compacted.push({
|
|
262
|
+
...projected,
|
|
263
|
+
_context_delta: `rehydrated@${watermark}`
|
|
264
|
+
});
|
|
265
|
+
rehydratedCount += 1;
|
|
266
|
+
telemetry.rehydrationsEmitted += 1;
|
|
267
|
+
} else {
|
|
268
|
+
sess.entityMap.set(id, {
|
|
269
|
+
...prev,
|
|
270
|
+
consecutiveDeltas: nextConsecutive
|
|
271
|
+
});
|
|
272
|
+
compacted.push(adapter.projectStub(rec, watermark));
|
|
273
|
+
unchangedCount += 1;
|
|
274
|
+
telemetry.deltasEmitted += 1;
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
const { changedFields, diffValues } = computeDiff(prev.snapshot, projected);
|
|
278
|
+
sess.entityMap.set(id, {
|
|
279
|
+
watermark,
|
|
280
|
+
snapshot: projected,
|
|
281
|
+
consecutiveDeltas: 0
|
|
282
|
+
});
|
|
283
|
+
compacted.push({
|
|
284
|
+
[adapter.idField]: id,
|
|
285
|
+
_context_delta: `updated@${watermark}`,
|
|
286
|
+
_changed_fields: changedFields,
|
|
287
|
+
...diffValues
|
|
288
|
+
});
|
|
289
|
+
updatedCount += 1;
|
|
290
|
+
telemetry.deltasEmitted += 1;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const outEnvelope = {
|
|
295
|
+
_mcp: {
|
|
296
|
+
server: "postbuild-mcp@2.1.0",
|
|
297
|
+
adapter: adapter.name,
|
|
298
|
+
session_id: sessionId,
|
|
299
|
+
turn: sess.turns,
|
|
300
|
+
cache_hit: Boolean(records._mcp_cache_hit),
|
|
301
|
+
query_ms: records._mcp_query_ms ?? 1.1,
|
|
302
|
+
counts: {
|
|
303
|
+
total: compacted.length,
|
|
304
|
+
initial: initialCount,
|
|
305
|
+
unchanged_stubs: unchangedCount,
|
|
306
|
+
updated_deltas: updatedCount,
|
|
307
|
+
rehydrated: rehydratedCount
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
records: compacted
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const compactBytes = Buffer.byteLength(JSON.stringify(outEnvelope), "utf8");
|
|
314
|
+
telemetry.compactedBytesEmitted += compactBytes;
|
|
315
|
+
const savingsPct =
|
|
316
|
+
rawBytes > 0 ? Number((((rawBytes - compactBytes) / rawBytes) * 100).toFixed(1)) : 0;
|
|
317
|
+
outEnvelope._mcp.payload_bytes = {
|
|
318
|
+
raw_stateless_bytes: rawBytes,
|
|
319
|
+
compacted_bytes: compactBytes,
|
|
320
|
+
reduction_pct: savingsPct > 0 ? `-${savingsPct}%` : "0%"
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
return outEnvelope;
|
|
324
|
+
},
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Two-Phase Cryptographic Write Guard (Optimistic Concurrency Control).
|
|
328
|
+
*/
|
|
329
|
+
evaluateWriteMutation({
|
|
330
|
+
sessionId = "global-default",
|
|
331
|
+
principalId = "default",
|
|
332
|
+
entityId,
|
|
333
|
+
liveRecord,
|
|
334
|
+
expectedWatermark,
|
|
335
|
+
changes = {},
|
|
336
|
+
dryRun = true,
|
|
337
|
+
commitToken
|
|
338
|
+
}) {
|
|
339
|
+
if (!liveRecord) {
|
|
340
|
+
telemetry.occRejections += 1;
|
|
341
|
+
return { allowed: false, reason: `Entity '${entityId}' not found.` };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const liveWatermark = adapter.getWatermark(liveRecord);
|
|
345
|
+
if (expectedWatermark && expectedWatermark !== liveWatermark) {
|
|
346
|
+
telemetry.occRejections += 1;
|
|
347
|
+
return {
|
|
348
|
+
allowed: false,
|
|
349
|
+
reason: `OCC Watermark mismatch on '${entityId}': expected '${expectedWatermark}', but live record is at '${liveWatermark}'. Re-read the entity before mutating.`
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const previewDiff = {};
|
|
354
|
+
for (const [k, newVal] of Object.entries(changes)) {
|
|
355
|
+
previewDiff[k] = {
|
|
356
|
+
before: liveRecord[k] ?? null,
|
|
357
|
+
after: newVal
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const changeHash = crypto
|
|
362
|
+
.createHash("sha256")
|
|
363
|
+
.update(JSON.stringify({ entityId, liveWatermark, changes }))
|
|
364
|
+
.digest("hex")
|
|
365
|
+
.slice(0, 16);
|
|
366
|
+
|
|
367
|
+
if (dryRun) {
|
|
368
|
+
telemetry.occDryRuns += 1;
|
|
369
|
+
const nonce = crypto.randomBytes(6).toString("hex");
|
|
370
|
+
const rawPayload = `${sessionId}|${principalId}|${entityId}|${liveWatermark}|${changeHash}|${nonce}`;
|
|
371
|
+
const sig = crypto
|
|
372
|
+
.createHmac("sha256", DEFAULT_SECRET)
|
|
373
|
+
.update(rawPayload)
|
|
374
|
+
.digest("hex")
|
|
375
|
+
.slice(0, 20);
|
|
376
|
+
const token = `ct_${nonce}_${sig}`;
|
|
377
|
+
|
|
378
|
+
ticketVault.set(token, {
|
|
379
|
+
sessionId,
|
|
380
|
+
principalId,
|
|
381
|
+
entityId: String(entityId),
|
|
382
|
+
expectedWatermark: liveWatermark,
|
|
383
|
+
changeHash,
|
|
384
|
+
expiresAt: Date.now() + commitTokenTtlMs,
|
|
385
|
+
used: false
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
return {
|
|
389
|
+
allowed: true,
|
|
390
|
+
dryRun: true,
|
|
391
|
+
liveWatermark,
|
|
392
|
+
previewDiff,
|
|
393
|
+
commitToken: token,
|
|
394
|
+
expiresInSeconds: Math.round(commitTokenTtlMs / 1000)
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (requireCommitTokenOnWrite) {
|
|
399
|
+
if (!commitToken || !ticketVault.has(commitToken)) {
|
|
400
|
+
telemetry.occRejections += 1;
|
|
401
|
+
return {
|
|
402
|
+
allowed: false,
|
|
403
|
+
reason:
|
|
404
|
+
"Missing or unknown commit_token. Run this tool with dry_run: true first to obtain a cryptographic Two-Phase Commit token."
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const ticket = ticketVault.get(commitToken);
|
|
409
|
+
if (ticket.used) {
|
|
410
|
+
telemetry.occRejections += 1;
|
|
411
|
+
return {
|
|
412
|
+
allowed: false,
|
|
413
|
+
reason: "Replay rejected: commit_token has already been consumed."
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
if (Date.now() > ticket.expiresAt) {
|
|
417
|
+
ticketVault.delete(commitToken);
|
|
418
|
+
telemetry.occRejections += 1;
|
|
419
|
+
return {
|
|
420
|
+
allowed: false,
|
|
421
|
+
reason: "Expired commit_token (TTL > 5 minutes). Re-run with dry_run: true."
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
if (ticket.sessionId !== sessionId || ticket.principalId !== principalId) {
|
|
425
|
+
telemetry.occRejections += 1;
|
|
426
|
+
return {
|
|
427
|
+
allowed: false,
|
|
428
|
+
reason: "Principal binding violation: commit_token belongs to a different session or principal."
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
if (ticket.entityId !== String(entityId) || ticket.expectedWatermark !== liveWatermark) {
|
|
432
|
+
telemetry.occRejections += 1;
|
|
433
|
+
return {
|
|
434
|
+
allowed: false,
|
|
435
|
+
reason: `Concurrent modification detected: entity '${entityId}' changed since dry_run token was issued.`
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
if (ticket.changeHash !== changeHash) {
|
|
439
|
+
telemetry.occRejections += 1;
|
|
440
|
+
return {
|
|
441
|
+
allowed: false,
|
|
442
|
+
reason: "Payload mismatch: mutation parameters differ from the approved dry_run preview."
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
ticket.used = true;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
telemetry.occCommits += 1;
|
|
450
|
+
return {
|
|
451
|
+
allowed: true,
|
|
452
|
+
dryRun: false,
|
|
453
|
+
committedWatermark: liveWatermark,
|
|
454
|
+
previewDiff
|
|
455
|
+
};
|
|
456
|
+
},
|
|
457
|
+
|
|
458
|
+
invalidateEntityCache(sessionId = "global-default", entityId = null, principalId = "default") {
|
|
459
|
+
const sess = getSession(sessionId, principalId);
|
|
460
|
+
sess.supersetCache.clear();
|
|
461
|
+
if (entityId) {
|
|
462
|
+
sess.entityMap.delete(String(entityId));
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
resetSession(sessionId = "global-default", principalId = "default") {
|
|
467
|
+
sessions.delete(`${sessionId}::${principalId}`);
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
getTelemetrySummary() {
|
|
471
|
+
const savedBytes = Math.max(0, telemetry.rawBytesEstimated - telemetry.compactedBytesEmitted);
|
|
472
|
+
const savedPct =
|
|
473
|
+
telemetry.rawBytesEstimated > 0
|
|
474
|
+
? Number(((savedBytes / telemetry.rawBytesEstimated) * 100).toFixed(1))
|
|
475
|
+
: 0;
|
|
476
|
+
return {
|
|
477
|
+
server: "postbuild-mcp@2.1.0",
|
|
478
|
+
adapter: adapter.name,
|
|
479
|
+
...telemetry,
|
|
480
|
+
bytesSaved: savedBytes,
|
|
481
|
+
tokenReductionPct: `-${savedPct}%`
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
}
|