trimprompt 1.0.29 → 1.0.31
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 +13 -0
- package/api-proxy.js +1 -1
- package/cache-manager.js +1 -0
- package/cache.js +1 -1
- package/ccr.js +1 -1
- package/cli.js +1 -1
- package/dashboard.js +1 -1
- package/executor.js +1 -1
- package/file-watcher.js +1 -0
- package/filters/devops.js +1 -1
- package/filters/generic.js +1 -1
- package/filters/git.js +1 -1
- package/filters/go.js +99 -1
- package/filters/index.js +185 -1
- package/filters/js.js +78 -1
- package/filters/python.js +130 -1
- package/filters/rust.js +115 -1
- package/filters/shell.js +395 -1
- package/hooks/claude-hook.js +79 -0
- package/index.html +339 -119
- package/mcp.js +132 -1
- package/package.json +1 -4
- package/proxy-conv.js +181 -1
- package/proxy-daemon.js +382 -1
- package/proxy-resp.js +1 -1
- package/redactor.js +1 -1
- package/seed.js +1 -1
- package/shims.js +1 -1
- package/simulate.js +1 -1
- package/sync.js +1 -1
- package/tracker.js +1 -1
- package/traffic-interceptor.js +1 -1
package/mcp.js
CHANGED
|
@@ -1 +1,132 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runMcpProxy = runMcpProxy;
|
|
37
|
+
const child_process_1 = require("child_process");
|
|
38
|
+
const readline = __importStar(require("readline"));
|
|
39
|
+
const filters_1 = require("./filters");
|
|
40
|
+
const tracker_1 = require("./tracker");
|
|
41
|
+
const proxy_conv_1 = require("./proxy-conv");
|
|
42
|
+
const proxy_resp_1 = require("./proxy-resp");
|
|
43
|
+
function runMcpProxy(command, args) {
|
|
44
|
+
const child = (0, child_process_1.spawn)(command, args, {
|
|
45
|
+
stdio: ['pipe', 'pipe', 'inherit']
|
|
46
|
+
});
|
|
47
|
+
const config = tracker_1.tracker.getConfig();
|
|
48
|
+
const featuresEnabled = config.features_enabled || {};
|
|
49
|
+
const convEnabled = featuresEnabled.conv_compress !== false;
|
|
50
|
+
const respEnabled = featuresEnabled.resp_optimize !== false;
|
|
51
|
+
const stdinRl = readline.createInterface({ input: process.stdin, terminal: false });
|
|
52
|
+
stdinRl.on('line', (line) => {
|
|
53
|
+
try {
|
|
54
|
+
const msg = JSON.parse(line);
|
|
55
|
+
if (msg && msg.jsonrpc === '2.0' && msg.method && msg.params) {
|
|
56
|
+
const params = msg.params;
|
|
57
|
+
if (Array.isArray(params.messages) && params.messages.length > 0) {
|
|
58
|
+
if (convEnabled) {
|
|
59
|
+
const result = (0, proxy_conv_1.compressConversation)(params.messages);
|
|
60
|
+
if (result.trimmedCount > 0 || result.savedTokens > 0) {
|
|
61
|
+
params.messages = result.messages;
|
|
62
|
+
tracker_1.tracker.logExecution('proxy:conv-compress', `${result.trimmedCount + result.messages.length} messages`, `${result.messages.length} messages (${result.trimmedCount} trimmed, ~${result.savedTokens} tokens saved)`, 0, undefined, ['conv_compress']);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (respEnabled) {
|
|
66
|
+
const opt = (0, proxy_resp_1.optimizeMaxTokens)(params.messages, params.max_tokens);
|
|
67
|
+
if (opt) {
|
|
68
|
+
const prevMax = params.max_tokens || 'default';
|
|
69
|
+
params.max_tokens = opt.max_tokens;
|
|
70
|
+
tracker_1.tracker.logExecution('proxy:resp-optimize', `max_tokens: ${prevMax}`, `max_tokens: ${opt.max_tokens} (${opt.complexity})`, 0, undefined, ['resp_optimize']);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
child.stdin.write(JSON.stringify(msg) + '\n');
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
child.stdin.write(line + '\n');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
child.stdin.write(line + '\n');
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
stdinRl.on('close', () => { try {
|
|
85
|
+
child.stdin.end();
|
|
86
|
+
}
|
|
87
|
+
catch { } });
|
|
88
|
+
const rl = readline.createInterface({
|
|
89
|
+
input: child.stdout,
|
|
90
|
+
terminal: false
|
|
91
|
+
});
|
|
92
|
+
rl.on('line', (line) => {
|
|
93
|
+
try {
|
|
94
|
+
const msg = JSON.parse(line);
|
|
95
|
+
if (msg &&
|
|
96
|
+
msg.jsonrpc === '2.0' &&
|
|
97
|
+
msg.id !== undefined &&
|
|
98
|
+
msg.result &&
|
|
99
|
+
Array.isArray(msg.result.content)) {
|
|
100
|
+
let textCompacted = false;
|
|
101
|
+
for (const item of msg.result.content) {
|
|
102
|
+
if (item.type === 'text' && typeof item.text === 'string' && item.text.trim().length > 0) {
|
|
103
|
+
const rawText = item.text;
|
|
104
|
+
const compressedText = (0, filters_1.compressOutput)('mcp-tool-call', rawText, '', tracker_1.tracker.getConfig().redaction_enabled);
|
|
105
|
+
tracker_1.tracker.logExecution(`mcp tool (id: ${msg.id})`, rawText, compressedText, 0);
|
|
106
|
+
item.text = compressedText;
|
|
107
|
+
textCompacted = true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (textCompacted) {
|
|
111
|
+
process.stdout.write(JSON.stringify(msg) + '\n');
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
process.stdout.write(line + '\n');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
process.stdout.write(line + '\n');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
process.stdout.write(line + '\n');
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
child.on('close', (code) => {
|
|
126
|
+
process.exit(code || 0);
|
|
127
|
+
});
|
|
128
|
+
child.on('error', (err) => {
|
|
129
|
+
console.error(`[TrimPrompt] MCP Server Spawn Error: ${err.message}`);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
});
|
|
132
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trimprompt",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.31",
|
|
4
4
|
"description": "TrimPrompt — Save 80% on LLM tokens. Zero-config CLI proxy for AI coding agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"llm",
|
|
@@ -24,9 +24,6 @@
|
|
|
24
24
|
"trim": "cli.js"
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://trimprompt.ai",
|
|
27
|
-
"scripts": {
|
|
28
|
-
"postinstall": "node cli.js shims install || true"
|
|
29
|
-
},
|
|
30
27
|
"dependencies": {
|
|
31
28
|
"commander": "^11.1.0",
|
|
32
29
|
"express": "^4.18.2",
|
package/proxy-conv.js
CHANGED
|
@@ -1 +1,181 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.compressConversation = compressConversation;
|
|
4
|
+
const ROLE_WEIGHT = {
|
|
5
|
+
system: 1.0,
|
|
6
|
+
user: 0.8,
|
|
7
|
+
assistant: 0.5,
|
|
8
|
+
tool: 0.3,
|
|
9
|
+
};
|
|
10
|
+
function estimateTokens(text) {
|
|
11
|
+
return Math.ceil(text.length / 4);
|
|
12
|
+
}
|
|
13
|
+
function blockText(b) {
|
|
14
|
+
if (!b || typeof b !== 'object')
|
|
15
|
+
return '';
|
|
16
|
+
if (b.type === 'text' && typeof b.text === 'string')
|
|
17
|
+
return b.text;
|
|
18
|
+
if (b.type === 'tool_result') {
|
|
19
|
+
if (typeof b.content === 'string')
|
|
20
|
+
return b.content;
|
|
21
|
+
if (Array.isArray(b.content))
|
|
22
|
+
return b.content.map(blockText).filter(Boolean).join('\n');
|
|
23
|
+
}
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
function messageText(msg) {
|
|
27
|
+
if (typeof msg.content === 'string')
|
|
28
|
+
return msg.content;
|
|
29
|
+
if (Array.isArray(msg.content)) {
|
|
30
|
+
return msg.content.map(blockText).filter(Boolean).join('\n');
|
|
31
|
+
}
|
|
32
|
+
return '';
|
|
33
|
+
}
|
|
34
|
+
function summarizeText(text) {
|
|
35
|
+
const tokens = estimateTokens(text);
|
|
36
|
+
const lines = text.split('\n');
|
|
37
|
+
if (tokens > 2000) {
|
|
38
|
+
if (lines.length > 10) {
|
|
39
|
+
const head = lines.slice(0, 5).join('\n');
|
|
40
|
+
const tail = lines.slice(-5).join('\n');
|
|
41
|
+
return `${head}\n[... ${lines.length - 10} lines trimmed by TrimPrompt ...]\n${tail}`;
|
|
42
|
+
}
|
|
43
|
+
return text.slice(0, 2000) + `\n[... ${text.length - 2000} chars trimmed by TrimPrompt ...]`;
|
|
44
|
+
}
|
|
45
|
+
if (tokens > 500) {
|
|
46
|
+
if (lines.length > 10) {
|
|
47
|
+
return lines.slice(0, 10).join('\n') + `\n[... ${lines.length - 10} lines trimmed ...]`;
|
|
48
|
+
}
|
|
49
|
+
return text.slice(0, 800) + `\n[... ${text.length - 800} chars trimmed ...]`;
|
|
50
|
+
}
|
|
51
|
+
return text;
|
|
52
|
+
}
|
|
53
|
+
function summarizeBlock(b) {
|
|
54
|
+
if (!b || typeof b !== 'object')
|
|
55
|
+
return b;
|
|
56
|
+
if (b.type === 'text' && typeof b.text === 'string') {
|
|
57
|
+
return { ...b, text: summarizeText(b.text) };
|
|
58
|
+
}
|
|
59
|
+
if (b.type === 'tool_result') {
|
|
60
|
+
if (typeof b.content === 'string')
|
|
61
|
+
return { ...b, content: summarizeText(b.content) };
|
|
62
|
+
if (Array.isArray(b.content)) {
|
|
63
|
+
const idx = largestBlockIndex(b.content);
|
|
64
|
+
if (idx >= 0) {
|
|
65
|
+
const content = b.content.map((c, i) => (i === idx ? summarizeBlock(c) : c));
|
|
66
|
+
return { ...b, content };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return b;
|
|
71
|
+
}
|
|
72
|
+
function largestBlockIndex(blocks) {
|
|
73
|
+
let idx = -1, max = -1;
|
|
74
|
+
blocks.forEach((b, i) => {
|
|
75
|
+
const len = blockText(b).length;
|
|
76
|
+
if (len > max) {
|
|
77
|
+
max = len;
|
|
78
|
+
idx = i;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return max > 0 ? idx : -1;
|
|
82
|
+
}
|
|
83
|
+
function scoreMessage(msg, index, total) {
|
|
84
|
+
const roleW = ROLE_WEIGHT[msg.role] || 0.4;
|
|
85
|
+
const recency = (index + 1) / total;
|
|
86
|
+
const text = messageText(msg);
|
|
87
|
+
const tokens = estimateTokens(text);
|
|
88
|
+
const lengthPenalty = tokens > 2000 ? 0.6 : tokens > 500 ? 0.8 : 1.0;
|
|
89
|
+
return roleW * (0.3 + 0.7 * recency) * lengthPenalty;
|
|
90
|
+
}
|
|
91
|
+
function summarizeMessage(msg) {
|
|
92
|
+
const tokens = estimateTokens(messageText(msg));
|
|
93
|
+
if (tokens <= 500)
|
|
94
|
+
return msg;
|
|
95
|
+
if (typeof msg.content === 'string') {
|
|
96
|
+
return { ...msg, content: summarizeText(msg.content) };
|
|
97
|
+
}
|
|
98
|
+
if (Array.isArray(msg.content)) {
|
|
99
|
+
const idx = largestBlockIndex(msg.content);
|
|
100
|
+
if (idx >= 0) {
|
|
101
|
+
const newContent = msg.content.map((b, i) => (i === idx ? summarizeBlock(b) : b));
|
|
102
|
+
return { ...msg, content: newContent };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return msg;
|
|
106
|
+
}
|
|
107
|
+
function compressConversation(messages, budgetTokens) {
|
|
108
|
+
const passthrough = { messages, trimmedCount: 0, savedTokens: 0 };
|
|
109
|
+
if (!messages || !Array.isArray(messages) || messages.length <= 6) {
|
|
110
|
+
return passthrough;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
return _compressConversation(messages, budgetTokens);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return passthrough;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function _compressConversation(messages, budgetTokens) {
|
|
120
|
+
const budget = budgetTokens || 80000;
|
|
121
|
+
const keepTailCount = 4;
|
|
122
|
+
const systemMsgs = messages.filter(m => m.role === 'system');
|
|
123
|
+
const nonSystem = messages.filter(m => m.role !== 'system');
|
|
124
|
+
const tail = nonSystem.slice(-keepTailCount);
|
|
125
|
+
const middle = nonSystem.slice(0, -keepTailCount);
|
|
126
|
+
const scored = middle.map((msg, i) => ({
|
|
127
|
+
msg,
|
|
128
|
+
score: scoreMessage(msg, i, middle.length),
|
|
129
|
+
tokens: estimateTokens(messageText(msg)),
|
|
130
|
+
}));
|
|
131
|
+
const totalTokens = scored.reduce((s, m) => s + m.tokens, 0);
|
|
132
|
+
const tailTokens = tail.reduce((s, m) => s + estimateTokens(messageText(m)), 0);
|
|
133
|
+
const sysTokens = systemMsgs.reduce((s, m) => s + estimateTokens(messageText(m)), 0);
|
|
134
|
+
const availBudget = budget - tailTokens - sysTokens;
|
|
135
|
+
if (totalTokens <= availBudget) {
|
|
136
|
+
let savedTokens = 0;
|
|
137
|
+
const compressed = scored.map(s => {
|
|
138
|
+
if (s.tokens > 500) {
|
|
139
|
+
const summarized = summarizeMessage(s.msg);
|
|
140
|
+
const newTokens = estimateTokens(messageText(summarized));
|
|
141
|
+
if (newTokens < s.tokens) {
|
|
142
|
+
savedTokens += s.tokens - newTokens;
|
|
143
|
+
return summarized;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return s.msg;
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
messages: [...systemMsgs, ...compressed, ...tail],
|
|
150
|
+
trimmedCount: 0,
|
|
151
|
+
savedTokens,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
scored.sort((a, b) => b.score - a.score);
|
|
155
|
+
let usedTokens = 0;
|
|
156
|
+
let trimmedCount = 0;
|
|
157
|
+
let savedTokens = 0;
|
|
158
|
+
const kept = [];
|
|
159
|
+
for (const s of scored) {
|
|
160
|
+
if (usedTokens + s.tokens <= availBudget) {
|
|
161
|
+
if (s.tokens > 1000) {
|
|
162
|
+
const summarized = summarizeMessage(s.msg);
|
|
163
|
+
const newTokens = estimateTokens(messageText(summarized));
|
|
164
|
+
savedTokens += s.tokens - newTokens;
|
|
165
|
+
usedTokens += newTokens;
|
|
166
|
+
kept.push({ msg: summarized, originalIndex: middle.indexOf(s.msg) });
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
usedTokens += s.tokens;
|
|
170
|
+
kept.push({ msg: s.msg, originalIndex: middle.indexOf(s.msg) });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
trimmedCount++;
|
|
175
|
+
savedTokens += s.tokens;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
kept.sort((a, b) => a.originalIndex - b.originalIndex);
|
|
179
|
+
const result = [...systemMsgs, ...kept.map(k => k.msg), ...tail];
|
|
180
|
+
return { messages: result, trimmedCount, savedTokens };
|
|
181
|
+
}
|