squeezr-ai 1.90.0 → 1.99.2
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/dist/__tests__/compressor.test.js +5 -1
- package/dist/__tests__/contentRouter.test.d.ts +1 -0
- package/dist/__tests__/contentRouter.test.js +76 -0
- package/dist/__tests__/controlEndpointGuard.test.d.ts +1 -0
- package/dist/__tests__/controlEndpointGuard.test.js +61 -0
- package/dist/__tests__/doctor.test.js +17 -1
- package/dist/__tests__/jsonCrush.test.js +59 -1
- package/dist/__tests__/outputSavings.test.d.ts +1 -0
- package/dist/__tests__/outputSavings.test.js +49 -0
- package/dist/__tests__/relevance.test.d.ts +1 -0
- package/dist/__tests__/relevance.test.js +66 -0
- package/dist/__tests__/secureKeyFile.test.d.ts +1 -0
- package/dist/__tests__/secureKeyFile.test.js +27 -0
- package/dist/__tests__/serverBinding.test.d.ts +1 -0
- package/dist/__tests__/serverBinding.test.js +18 -0
- package/dist/__tests__/statsOutput.test.d.ts +1 -0
- package/dist/__tests__/statsOutput.test.js +46 -0
- package/dist/__tests__/textCrusher.test.js +63 -1
- package/dist/codexMitm.d.ts +1 -0
- package/dist/codexMitm.js +26 -2
- package/dist/compressor.js +31 -3
- package/dist/contentRouter.d.ts +30 -0
- package/dist/contentRouter.js +112 -0
- package/dist/dashboard.d.ts +1 -1
- package/dist/dashboard.js +26 -7
- package/dist/deterministic.d.ts +1 -1
- package/dist/deterministic.js +14 -13
- package/dist/doctor.d.ts +5 -0
- package/dist/doctor.js +11 -0
- package/dist/index.js +4 -1
- package/dist/jsonCrush.d.ts +4 -0
- package/dist/jsonCrush.js +83 -18
- package/dist/outputSavings.d.ts +20 -0
- package/dist/outputSavings.js +52 -0
- package/dist/relevance.d.ts +27 -0
- package/dist/relevance.js +78 -0
- package/dist/server.js +84 -11
- package/dist/stats.d.ts +17 -0
- package/dist/stats.js +55 -0
- package/dist/systemPrompt.js +3 -2
- package/dist/textCrusher.d.ts +4 -0
- package/dist/textCrusher.js +69 -7
- package/package.json +69 -69
package/dist/compressor.js
CHANGED
|
@@ -206,10 +206,12 @@ async function compressWithGptMini(text, apiKey, extra) {
|
|
|
206
206
|
return resp.choices[0].message.content ?? '';
|
|
207
207
|
}
|
|
208
208
|
async function compressWithGeminiFlash(text, apiKey, extra) {
|
|
209
|
-
|
|
209
|
+
// Pass the key in the x-goog-api-key header, never in the URL query string:
|
|
210
|
+
// URLs leak into logs, proxies and error traces where a secret must not appear.
|
|
211
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_FLASH_MODEL}:generateContent`;
|
|
210
212
|
const resp = await fetch(url, {
|
|
211
213
|
method: 'POST',
|
|
212
|
-
headers: { 'content-type': 'application/json' },
|
|
214
|
+
headers: { 'content-type': 'application/json', 'x-goog-api-key': apiKey },
|
|
213
215
|
body: JSON.stringify({
|
|
214
216
|
contents: [{ role: 'user', parts: [{ text: `${promptWith(extra)}\n\n---\n${text.slice(0, OLLAMA_SAFE_INPUT)}` }] }],
|
|
215
217
|
}),
|
|
@@ -663,6 +665,25 @@ function buildAnthropicToolIdMap(messages) {
|
|
|
663
665
|
}
|
|
664
666
|
return { nameMap, skipIds };
|
|
665
667
|
}
|
|
668
|
+
// Most recent user-typed text across the conversation (skips tool-result-only turns),
|
|
669
|
+
// used as the relevance query for deterministic crushing. Capped so it stays cheap.
|
|
670
|
+
function lastUserText(messages) {
|
|
671
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
672
|
+
const m = messages[i];
|
|
673
|
+
if (m.role !== 'user')
|
|
674
|
+
continue;
|
|
675
|
+
if (typeof m.content === 'string')
|
|
676
|
+
return m.content.slice(0, 2000);
|
|
677
|
+
if (Array.isArray(m.content)) {
|
|
678
|
+
const texts = m.content
|
|
679
|
+
.filter(b => b && b.type === 'text' && typeof b.text === 'string')
|
|
680
|
+
.map(b => b.text);
|
|
681
|
+
if (texts.length > 0)
|
|
682
|
+
return texts.join(' ').slice(0, 2000);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return '';
|
|
686
|
+
}
|
|
666
687
|
export async function compressAnthropicMessages(messages, apiKey, config, systemExtraChars = 0) {
|
|
667
688
|
if (config.disabled)
|
|
668
689
|
return [messages, emptySavings()];
|
|
@@ -792,12 +813,19 @@ export async function compressAnthropicMessages(messages, apiKey, config, system
|
|
|
792
813
|
// changes every turn = cache miss = the 2026-06-04 over-bill). One cache miss
|
|
793
814
|
// happens the first time the level changes; stable forever after.
|
|
794
815
|
const DET_PRESSURE = 0;
|
|
816
|
+
// BM25 relevance in the deterministic pass is QUERY-dependent → it changes when the
|
|
817
|
+
// user's task changes. That is ONLY cache-safe where the output is not part of a
|
|
818
|
+
// byte-stable cached prefix. So we feed a query ONLY when there are NO cache markers
|
|
819
|
+
// (non-caching clients); for cache-marker clients (Claude Code) query = '' keeps the
|
|
820
|
+
// pass byte-identical between requests. (Relevance for the cached path needs per-block
|
|
821
|
+
// content-hash freezing — a follow-up.)
|
|
822
|
+
const detQuery = hasCacheMarkers ? '' : lastUserText(messages);
|
|
795
823
|
const detT0 = Date.now();
|
|
796
824
|
let detSaved = 0;
|
|
797
825
|
for (const { index, subIndex, text, tool } of allResults) {
|
|
798
826
|
if (dedupedSet.has(`${index}:${subIndex}`))
|
|
799
827
|
continue; // already replaced by dedup
|
|
800
|
-
const det = preprocessForTool(text, tool, DET_PRESSURE);
|
|
828
|
+
const det = preprocessForTool(text, tool, DET_PRESSURE, detQuery);
|
|
801
829
|
if (det !== text) {
|
|
802
830
|
;
|
|
803
831
|
msgs[index].content[subIndex].content = det;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content router — extends the JSON crusher's reach from "the WHOLE tool result is a JSON
|
|
3
|
+
* array" to "a JSON array appears ANYWHERE inside the tool result". Squeezr's take on the
|
|
4
|
+
* routing idea behind headroom's ContentRouter.
|
|
5
|
+
*
|
|
6
|
+
* A lot of structured data arrives wrapped: `gh api` with a status line, an MCP tool that
|
|
7
|
+
* returns a labelled envelope, a bash command that echoes a header then the JSON. Before,
|
|
8
|
+
* jsonCrush only fired on a pure-JSON result, so those were missed. This locates embedded
|
|
9
|
+
* arrays-of-objects and crushes each in place, leaving the surrounding text untouched.
|
|
10
|
+
*
|
|
11
|
+
* Safety: candidate spans are found by balanced-bracket scanning (string/escape aware),
|
|
12
|
+
* but the real safety net is that each span is handed to crushJsonArrays, which JSON.parses
|
|
13
|
+
* it — a false-positive span that isn't valid JSON simply doesn't crush and is left verbatim.
|
|
14
|
+
* Deterministic → cache-safe. Each crushed span is self-recoverable via its own expand id.
|
|
15
|
+
*/
|
|
16
|
+
import { TABLE_MARKER_RE } from './jsonCrush.js';
|
|
17
|
+
export { TABLE_MARKER_RE };
|
|
18
|
+
/** Non-overlapping, top-level spans of `[ {...`-style arrays (arrays of objects). */
|
|
19
|
+
export declare function findObjectArraySpans(text: string): Array<{
|
|
20
|
+
start: number;
|
|
21
|
+
end: number;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* Crush every embedded array-of-objects in `text`, splicing each crushed table back in and
|
|
25
|
+
* leaving the surrounding text verbatim. Returns the new text and total chars saved.
|
|
26
|
+
*/
|
|
27
|
+
export declare function crushEmbeddedJson(text: string): {
|
|
28
|
+
text: string;
|
|
29
|
+
savedChars: number;
|
|
30
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content router — extends the JSON crusher's reach from "the WHOLE tool result is a JSON
|
|
3
|
+
* array" to "a JSON array appears ANYWHERE inside the tool result". Squeezr's take on the
|
|
4
|
+
* routing idea behind headroom's ContentRouter.
|
|
5
|
+
*
|
|
6
|
+
* A lot of structured data arrives wrapped: `gh api` with a status line, an MCP tool that
|
|
7
|
+
* returns a labelled envelope, a bash command that echoes a header then the JSON. Before,
|
|
8
|
+
* jsonCrush only fired on a pure-JSON result, so those were missed. This locates embedded
|
|
9
|
+
* arrays-of-objects and crushes each in place, leaving the surrounding text untouched.
|
|
10
|
+
*
|
|
11
|
+
* Safety: candidate spans are found by balanced-bracket scanning (string/escape aware),
|
|
12
|
+
* but the real safety net is that each span is handed to crushJsonArrays, which JSON.parses
|
|
13
|
+
* it — a false-positive span that isn't valid JSON simply doesn't crush and is left verbatim.
|
|
14
|
+
* Deterministic → cache-safe. Each crushed span is self-recoverable via its own expand id.
|
|
15
|
+
*/
|
|
16
|
+
import { crushJsonArrays, TABLE_MARKER_RE } from './jsonCrush.js';
|
|
17
|
+
export { TABLE_MARKER_RE };
|
|
18
|
+
/** Scan from an opening '[' to its matching ']' (exclusive end), string/escape aware.
|
|
19
|
+
* Returns -1 if unbalanced. */
|
|
20
|
+
function scanArray(text, start) {
|
|
21
|
+
let depth = 0;
|
|
22
|
+
let inStr = false;
|
|
23
|
+
for (let i = start; i < text.length; i++) {
|
|
24
|
+
const c = text[i];
|
|
25
|
+
if (inStr) {
|
|
26
|
+
if (c === '\\') {
|
|
27
|
+
i++;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (c === '"')
|
|
31
|
+
inStr = false;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (c === '"') {
|
|
35
|
+
inStr = true;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (c === '[')
|
|
39
|
+
depth++;
|
|
40
|
+
else if (c === ']') {
|
|
41
|
+
depth--;
|
|
42
|
+
if (depth === 0)
|
|
43
|
+
return i + 1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return -1;
|
|
47
|
+
}
|
|
48
|
+
/** Non-overlapping, top-level spans of `[ {...`-style arrays (arrays of objects). */
|
|
49
|
+
export function findObjectArraySpans(text) {
|
|
50
|
+
const spans = [];
|
|
51
|
+
const n = text.length;
|
|
52
|
+
let i = 0;
|
|
53
|
+
let inStr = false;
|
|
54
|
+
while (i < n) {
|
|
55
|
+
const c = text[i];
|
|
56
|
+
if (inStr) {
|
|
57
|
+
if (c === '\\') {
|
|
58
|
+
i += 2;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (c === '"')
|
|
62
|
+
inStr = false;
|
|
63
|
+
i++;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (c === '"') {
|
|
67
|
+
inStr = true;
|
|
68
|
+
i++;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (c === '[') {
|
|
72
|
+
// peek past whitespace for '{' → array of objects
|
|
73
|
+
let j = i + 1;
|
|
74
|
+
while (j < n && /\s/.test(text[j]))
|
|
75
|
+
j++;
|
|
76
|
+
if (text[j] === '{') {
|
|
77
|
+
const end = scanArray(text, i);
|
|
78
|
+
if (end > i) {
|
|
79
|
+
spans.push({ start: i, end });
|
|
80
|
+
i = end;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
i++;
|
|
86
|
+
}
|
|
87
|
+
return spans;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Crush every embedded array-of-objects in `text`, splicing each crushed table back in and
|
|
91
|
+
* leaving the surrounding text verbatim. Returns the new text and total chars saved.
|
|
92
|
+
*/
|
|
93
|
+
export function crushEmbeddedJson(text) {
|
|
94
|
+
const spans = findObjectArraySpans(text);
|
|
95
|
+
if (spans.length === 0)
|
|
96
|
+
return { text, savedChars: 0 };
|
|
97
|
+
let out = '';
|
|
98
|
+
let last = 0;
|
|
99
|
+
let saved = 0;
|
|
100
|
+
for (const { start, end } of spans) {
|
|
101
|
+
out += text.slice(last, start);
|
|
102
|
+
const sub = text.slice(start, end);
|
|
103
|
+
const crushed = crushJsonArrays(sub);
|
|
104
|
+
out += crushed.text;
|
|
105
|
+
saved += crushed.savedChars;
|
|
106
|
+
last = end;
|
|
107
|
+
}
|
|
108
|
+
out += text.slice(last);
|
|
109
|
+
if (saved <= 0)
|
|
110
|
+
return { text, savedChars: 0 };
|
|
111
|
+
return { text: out, savedChars: saved };
|
|
112
|
+
}
|