trimprompt 1.0.29 → 1.0.30
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/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 +1 -1
- package/filters/index.js +1 -1
- package/filters/js.js +1 -1
- package/filters/python.js +1 -1
- package/filters/rust.js +1 -1
- package/filters/shell.js +1 -1
- package/hooks/claude-hook.js +1 -0
- package/index.html +339 -119
- package/mcp.js +1 -1
- package/package.json +1 -1
- package/proxy-conv.js +1 -1
- package/proxy-daemon.js +1 -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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
'use strict';var a0_0x146269=a0_0x4b65;function a0_0x4b65(_0x127030,_0x2ab314){_0x127030=_0x127030-0x68;var _0x2e61ac=a0_0x2e61();var _0x4b6504=_0x2e61ac[_0x127030];return _0x4b6504;}(function(_0x25c015,_0x2ad984){var _0x155ef9=a0_0x4b65,_0x4c5c6e=_0x25c015();while(!![]){try{var _0x5b30bb=parseInt(_0x155ef9(0x74))/0x1+parseInt(_0x155ef9(0x85))/0x2+parseInt(_0x155ef9(0x81))/0x3+parseInt(_0x155ef9(0x6c))/0x4*(parseInt(_0x155ef9(0x83))/0x5)+-parseInt(_0x155ef9(0x6b))/0x6+-parseInt(_0x155ef9(0x73))/0x7+parseInt(_0x155ef9(0x69))/0x8*(-parseInt(_0x155ef9(0x79))/0x9);if(_0x5b30bb===_0x2ad984)break;else _0x4c5c6e['push'](_0x4c5c6e['shift']());}catch(_0x1cc49b){_0x4c5c6e['push'](_0x4c5c6e['shift']());}}}(a0_0x2e61,0xc3389));function a0_0x2e61(){var _0x3aea73=['__setModuleDefault','homedir','__importStar','.claude','4711239rDaAqp','__esModule','200uVZgbW','installClaudeHooks','1532524dhTuaC','getOwnPropertyDescriptor','hasOwnProperty','__createBinding','16412576xCHmiv','//\x20TrimPrompt\x20Native\x20PostToolUse\x20Hook\x20for\x20Claude\x20Code\x0aconst\x20fs\x20=\x20require(\x27fs\x27);\x0aconst\x20path\x20=\x20require(\x27path\x27);\x0aconst\x20os\x20=\x20require(\x27os\x27);\x0a\x0amodule.exports\x20=\x20async\x20function\x20postToolUse(event)\x20{\x0a\x20\x20try\x20{\x0a\x20\x20\x20\x20if\x20(!event\x20||\x20!event.result)\x20return\x20event;\x0a\x20\x20\x20\x20let\x20contentStr\x20=\x20typeof\x20event.result\x20===\x20\x27string\x27\x20?\x20event.result\x20:\x20JSON.stringify(event.result);\x0a\x20\x20\x20\x20if\x20(contentStr.length\x20>\x20500)\x20{\x0a\x20\x20\x20\x20\x20\x20const\x20cacheDir\x20=\x20path.join(os.homedir(),\x20\x27.trimprompt\x27,\x20\x27cache\x27);\x0a\x20\x20\x20\x20\x20\x20if\x20(!fs.existsSync(cacheDir))\x20fs.mkdirSync(cacheDir,\x20{\x20recursive:\x20true\x20});\x0a\x20\x20\x20\x20\x20\x20const\x20chunkId\x20=\x20\x27chunk_\x27\x20+\x20Math.random().toString(36).substring(2,\x2011);\x0a\x20\x20\x20\x20\x20\x20fs.writeFileSync(path.join(cacheDir,\x20chunkId\x20+\x20\x27.json\x27),\x20JSON.stringify({\x20raw_payload:\x20contentStr,\x20timestamp:\x20new\x20Date().toISOString()\x20}));\x0a\x20\x20\x20\x20\x20\x20\x0a\x20\x20\x20\x20\x20\x20const\x20snippet\x20=\x20contentStr.substring(0,\x20200).replace(/\x5cn/g,\x20\x27\x20\x27);\x0a\x20\x20\x20\x20\x20\x20const\x20replacement\x20=\x20`>\x20[!NOTE]\x5cn>\x20**[TrimPrompt\x20Context\x20Offloaded]**\x20Raw\x20tool\x20output\x20(${contentStr.length.toLocaleString()}\x20chars)\x20side-cached\x20locally.\x5cn>\x20Preview:\x20${snippet}...\x5cn>\x20Retrieve\x20raw:\x20`trim\x20retrieve\x20${chunkId}``;\x0a\x20\x20\x20\x20\x20\x20\x0a\x20\x20\x20\x20\x20\x20if\x20(typeof\x20event.result\x20===\x20\x27string\x27)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20event.result\x20=\x20replacement;\x0a\x20\x20\x20\x20\x20\x20}\x20else\x20if\x20(event.result\x20&&\x20typeof\x20event.result\x20===\x20\x27object\x27)\x20{\x0a\x20\x20\x20\x20\x20\x20\x20\x20event.result.content\x20=\x20replacement;\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20}\x20catch\x20(e)\x20{}\x0a\x20\x20return\x20event;\x0a};\x0a','1072776PYaLED','2488tBluhF','default','length','configurable','writeFileSync','mkdirSync','call','1042776rpjzCI','817406wNxklI','defineProperty','join','post_tool_use.js','path','9eZccpN','existsSync','utf8','writable'];a0_0x2e61=function(){return _0x3aea73;};return a0_0x2e61();}var __createBinding=this&&this[a0_0x146269(0x68)]||(Object['create']?function(_0x37a97b,_0x37112e,_0x3848e9,_0xeabf95){var _0x1943b3=a0_0x146269;if(_0xeabf95===undefined)_0xeabf95=_0x3848e9;var _0x23af02=Object[_0x1943b3(0x86)](_0x37112e,_0x3848e9);(!_0x23af02||('get'in _0x23af02?!_0x37112e[_0x1943b3(0x82)]:_0x23af02[_0x1943b3(0x7c)]||_0x23af02[_0x1943b3(0x6f)]))&&(_0x23af02={'enumerable':!![],'get':function(){return _0x37112e[_0x3848e9];}}),Object[_0x1943b3(0x75)](_0x37a97b,_0xeabf95,_0x23af02);}:function(_0x24547a,_0x1c0740,_0x67b1af,_0x15b727){if(_0x15b727===undefined)_0x15b727=_0x67b1af;_0x24547a[_0x15b727]=_0x1c0740[_0x67b1af];}),__setModuleDefault=this&&this[a0_0x146269(0x7d)]||(Object['create']?function(_0x1c3c42,_0x11f249){var _0x3ba327=a0_0x146269;Object[_0x3ba327(0x75)](_0x1c3c42,_0x3ba327(0x6d),{'enumerable':!![],'value':_0x11f249});}:function(_0x355c44,_0x11a08e){_0x355c44['default']=_0x11a08e;}),__importStar=this&&this[a0_0x146269(0x7f)]||(function(){var _0x1ae355=function(_0x253511){return _0x1ae355=Object['getOwnPropertyNames']||function(_0x2aa28a){var _0x268f23=a0_0x4b65,_0x224bc6=[];for(var _0x72d900 in _0x2aa28a)if(Object['prototype'][_0x268f23(0x87)][_0x268f23(0x72)](_0x2aa28a,_0x72d900))_0x224bc6[_0x224bc6[_0x268f23(0x6e)]]=_0x72d900;return _0x224bc6;},_0x1ae355(_0x253511);};return function(_0x21a2aa){var _0x309a51=a0_0x4b65;if(_0x21a2aa&&_0x21a2aa[_0x309a51(0x82)])return _0x21a2aa;var _0x5c259d={};if(_0x21a2aa!=null){for(var _0x2e0db7=_0x1ae355(_0x21a2aa),_0x2d0c48=0x0;_0x2d0c48<_0x2e0db7['length'];_0x2d0c48++)if(_0x2e0db7[_0x2d0c48]!==_0x309a51(0x6d))__createBinding(_0x5c259d,_0x21a2aa,_0x2e0db7[_0x2d0c48]);}return __setModuleDefault(_0x5c259d,_0x21a2aa),_0x5c259d;};}());Object['defineProperty'](exports,a0_0x146269(0x82),{'value':!![]}),exports[a0_0x146269(0x84)]=installClaudeHooks;const fs=__importStar(require('fs')),path=__importStar(require(a0_0x146269(0x78))),os=__importStar(require('os'));function installClaudeHooks(){var _0x16e177=a0_0x146269;try{const _0x3ef0e9=path[_0x16e177(0x76)](os[_0x16e177(0x7e)](),_0x16e177(0x80),'hooks');!fs[_0x16e177(0x7a)](_0x3ef0e9)&&fs[_0x16e177(0x71)](_0x3ef0e9,{'recursive':!![]});const _0x404dab=path[_0x16e177(0x76)](_0x3ef0e9,_0x16e177(0x77)),_0x25b97f=_0x16e177(0x6a);fs[_0x16e177(0x70)](_0x404dab,_0x25b97f,_0x16e177(0x7b));}catch(_0x2f114b){}}
|
package/index.html
CHANGED
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
flex-direction: column;
|
|
42
42
|
position: relative;
|
|
43
43
|
overflow-x: hidden;
|
|
44
|
+
zoom: 80%;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
/* Ambient radial glow background */
|
|
@@ -235,14 +236,16 @@
|
|
|
235
236
|
font-family: 'Outfit', sans-serif;
|
|
236
237
|
font-weight: 700;
|
|
237
238
|
color: var(--text-muted);
|
|
238
|
-
padding: 1rem;
|
|
239
|
+
padding: 1.1rem 1.2rem;
|
|
239
240
|
border-bottom: 1px solid var(--border);
|
|
241
|
+
vertical-align: middle;
|
|
240
242
|
}
|
|
241
243
|
|
|
242
244
|
td {
|
|
243
|
-
padding: 1rem;
|
|
245
|
+
padding: 1.1rem 1.2rem;
|
|
244
246
|
border-bottom: 1px solid var(--border);
|
|
245
247
|
color: var(--text);
|
|
248
|
+
vertical-align: middle;
|
|
246
249
|
}
|
|
247
250
|
|
|
248
251
|
tr:last-child td {
|
|
@@ -678,7 +681,13 @@
|
|
|
678
681
|
</h2>
|
|
679
682
|
</div>
|
|
680
683
|
<div id="csa-body" style="display:none;">
|
|
681
|
-
<div id="csa-list"
|
|
684
|
+
<div id="csa-list">
|
|
685
|
+
<div style="padding:40px 20px;text-align:center;color:#818CF8;font-size:13px;display:flex;align-items:center;justify-content:center;gap:12px;">
|
|
686
|
+
<style>@keyframes csaSpin { to { transform: rotate(360deg); } }</style>
|
|
687
|
+
<span style="width:18px;height:18px;border:2px solid #818CF8;border-top-color:transparent;border-radius:50%;display:inline-block;animation:csaSpin 0.8s linear infinite;"></span>
|
|
688
|
+
<span style="font-weight:500;">Loading Live Session Context Controls...</span>
|
|
689
|
+
</div>
|
|
690
|
+
</div>
|
|
682
691
|
<div class="csa-summary" id="csa-summary"></div>
|
|
683
692
|
</div>
|
|
684
693
|
</div>
|
|
@@ -740,6 +749,7 @@
|
|
|
740
749
|
<script>
|
|
741
750
|
let savingsChart = null;
|
|
742
751
|
let currentFilter = 'ai';
|
|
752
|
+
const expandedGroupKeys = new Set();
|
|
743
753
|
|
|
744
754
|
const FEATURE_META = {
|
|
745
755
|
filter: { label: 'Filter', color: '#6366F1', bg: 'rgba(99,102,241,0.12)' },
|
|
@@ -776,11 +786,17 @@
|
|
|
776
786
|
{ category: 'S — Security', features: [
|
|
777
787
|
{ key: 'redact', title: 'Secret redaction (20 patterns)', desc: 'AWS keys, API tokens, JWTs, DB strings auto-blocked', badges: ['Claude','Cursor','Copilot','Gemini','Antigravity'], statKey: 'secrets', statSub: 'secrets blocked', status: 'active' },
|
|
778
788
|
]},
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
789
|
+
{ category: 'V — CONVERSATION COMPRESSION (LIVE CONTEXT)', features: [
|
|
790
|
+
{ key: 'conv_claude', title: 'Claude Code Live Compression', desc: 'Compresses active sessions in ~/.claude/projects/ via side-cache', badges: ['Claude Code'], agentKey: 'claude', statSub: 'on conversation', status: 'active' },
|
|
791
|
+
{ key: 'conv_gemini', title: 'Google Gemini Code Assistant', desc: 'Compresses Gemini CLI & extension session logs in ~/.config/gemini/', badges: ['Gemini'], agentKey: 'gemini', statSub: 'on gemini logs', status: 'active' },
|
|
792
|
+
{ key: 'conv_codex', title: 'Codex Shared Memory Compression', desc: 'Compresses shared memory session logs with Claude', badges: ['Codex'], agentKey: 'codex', statSub: 'on shared memory', status: 'active' },
|
|
793
|
+
{ key: 'conv_cursor', title: 'Cursor Workspace State Compression', desc: 'Compresses workspaceStorage JSON/vscdb files in real-time', badges: ['Cursor'], agentKey: 'cursor', statSub: 'on workspace state', status: 'active' },
|
|
794
|
+
{ key: 'conv_aider', title: 'Aider Chat History Optimizer', desc: 'Compresses .aider.chat.history.md files automatically on launch', badges: ['Aider'], agentKey: 'aider', statSub: 'on markdown logs', status: 'active' },
|
|
795
|
+
{ key: 'conv_copilot', title: 'Copilot CLI Session Trimmer', desc: 'Compresses github-copilot-cli session state logs', badges: ['Copilot CLI'], agentKey: 'copilot', statSub: 'on CLI sessions', status: 'active' },
|
|
796
|
+
{ key: 'conv_openclaw', title: 'OpenClaw ContextEngine Plugin', desc: 'Runs as an internal ContextEngine plugin for OpenClaw pipeline', badges: ['OpenClaw'], agentKey: 'openclaw', statSub: 'on plugin context', status: 'active' },
|
|
797
|
+
{ key: 'conv_opencode', title: 'OpenCode Config & Proxy Optimizer', desc: 'Injects proxy config and compresses .opencode/sessions payloads', badges: ['OpenCode'], agentKey: 'opencode', statSub: 'on open sessions', status: 'active' },
|
|
798
|
+
{ key: 'conv_cortex', title: 'Cortex Code SDK Library Mode', desc: 'SDK wrapper library mode providing 60-65% token savings', badges: ['Cortex Code'], agentKey: 'cortex', statSub: 'on SDK memory', status: 'active' },
|
|
799
|
+
]},
|
|
784
800
|
];
|
|
785
801
|
|
|
786
802
|
let proxyRunning = false;
|
|
@@ -874,30 +890,52 @@
|
|
|
874
890
|
try { await fetch('/api/config/features', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(csaToggles) }); } catch {}
|
|
875
891
|
}
|
|
876
892
|
|
|
877
|
-
function renderCsaSection(logs) {
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
893
|
+
async function renderCsaSection(logs) {
|
|
894
|
+
try {
|
|
895
|
+
const modelSelect = document.getElementById('model-select');
|
|
896
|
+
const modelVal = modelSelect ? modelSelect.value : 'claude-opus-4-8';
|
|
897
|
+
const pricePerM = PRICING_TABLE[modelVal] || 3.00;
|
|
898
|
+
const totalRaw = logs.reduce((s, l) => s + (l.estimated_raw_tokens || 0), 0);
|
|
899
|
+
const totalComp = logs.reduce((s, l) => s + (l.estimated_compressed_tokens || 0), 0);
|
|
900
|
+
const totalSaved = logs.reduce((s, l) => s + (l.tokens_saved || 0), 0);
|
|
901
|
+
const totalSecrets = logs.reduce((s, l) => s + (l.secrets_found || 0), 0);
|
|
902
|
+
const savedUsd = (totalSaved * pricePerM) / 1000000;
|
|
903
|
+
const reductionPct = totalRaw > 0 ? ((totalSaved / totalRaw) * 100).toFixed(1) : '0.0';
|
|
904
|
+
|
|
905
|
+
const featureCounts = {};
|
|
906
|
+
const featureSavings = {};
|
|
907
|
+
logs.forEach(l => {
|
|
908
|
+
(l.features || []).forEach(f => {
|
|
909
|
+
featureCounts[f] = (featureCounts[f] || 0) + 1;
|
|
910
|
+
featureSavings[f] = (featureSavings[f] || 0) + (l.tokens_saved || 0);
|
|
911
|
+
});
|
|
912
|
+
if (l.command) {
|
|
913
|
+
const matched = CSA_FEATURES.flatMap(c => c.features).find(feat => feat.agentKey && (l.command.includes(`conv:${feat.agentKey}`) || l.command.includes(feat.agentKey)));
|
|
914
|
+
if (matched) {
|
|
915
|
+
featureCounts[matched.key] = (featureCounts[matched.key] || 0) + 1;
|
|
916
|
+
featureSavings[matched.key] = (featureSavings[matched.key] || 0) + (l.tokens_saved || 0);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
892
919
|
});
|
|
893
|
-
});
|
|
894
920
|
|
|
895
|
-
|
|
896
|
-
|
|
921
|
+
const list = document.getElementById('csa-list');
|
|
922
|
+
if (!list) return;
|
|
923
|
+
let html = '';
|
|
924
|
+
|
|
925
|
+
let globalConfigData = {};
|
|
926
|
+
try {
|
|
927
|
+
const cfgResp = await fetch('/api/config');
|
|
928
|
+
globalConfigData = await cfgResp.json();
|
|
929
|
+
} catch(e){}
|
|
897
930
|
|
|
898
931
|
CSA_FEATURES.forEach(cat => {
|
|
899
932
|
html += `<div class="csa-category">${cat.category}</div>`;
|
|
900
|
-
cat.features.
|
|
933
|
+
const sortedFeatures = [...cat.features].sort((a, b) => {
|
|
934
|
+
const savA = featureSavings[a.key] || 0;
|
|
935
|
+
const savB = featureSavings[b.key] || 0;
|
|
936
|
+
return savB - savA;
|
|
937
|
+
});
|
|
938
|
+
sortedFeatures.forEach(f => {
|
|
901
939
|
const isSoon = f.status === 'soon';
|
|
902
940
|
const isOff = f.status === 'off';
|
|
903
941
|
const defaultOn = f.status === 'active';
|
|
@@ -905,6 +943,7 @@
|
|
|
905
943
|
const count = featureCounts[f.key] || 0;
|
|
906
944
|
const statusLabels = { active: 'Active', off: 'Off by default', soon: 'Coming soon' };
|
|
907
945
|
let statVal, statColor;
|
|
946
|
+
let statSubText = f.statSub;
|
|
908
947
|
if (f.statKey === 'secrets') {
|
|
909
948
|
statVal = totalSecrets.toLocaleString();
|
|
910
949
|
statColor = '#EF4444';
|
|
@@ -916,21 +955,69 @@
|
|
|
916
955
|
const featureSaved = (featureSavedTokens * pricePerM) / 1000000;
|
|
917
956
|
statVal = '$' + featureSaved.toFixed(2);
|
|
918
957
|
statColor = '#10B981';
|
|
958
|
+
if (featureSavedTokens > 0) {
|
|
959
|
+
statSubText = `<span style="color:#10B981;font-weight:600;">⚡ ${featureSavedTokens.toLocaleString()} tokens saved</span>`;
|
|
960
|
+
}
|
|
919
961
|
}
|
|
920
962
|
const rowClass = isSoon ? 'csa-row is-soon' : isOff ? 'csa-row is-off' : 'csa-row';
|
|
963
|
+
|
|
964
|
+
let liveBadge = `<span class="csa-status ${f.status}">${statusLabels[f.status]}</span>`;
|
|
965
|
+
let latestFileHtml = '';
|
|
966
|
+
let claudeCompactHtml = '';
|
|
967
|
+
if (f.agentKey) {
|
|
968
|
+
const sess = globalConfigData.latestSessions && globalConfigData.latestSessions[f.agentKey];
|
|
969
|
+
let isToday = false;
|
|
970
|
+
if (sess && sess.lastModified) {
|
|
971
|
+
const fileDate = new Date(sess.lastModified).toDateString();
|
|
972
|
+
const todayDate = new Date().toDateString();
|
|
973
|
+
if (fileDate === todayDate) isToday = true;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
if (isToday) {
|
|
977
|
+
liveBadge = `<span style="background:rgba(16,185,129,0.25);color:#10B981;border:1px solid #10B981;padding:2px 10px;border-radius:12px;font-size:10px;font-weight:700;display:inline-flex;align-items:center;gap:5px;margin-left:8px;box-shadow:0 0 10px rgba(16,185,129,0.2);"><span style="width:7px;height:7px;border-radius:50%;background:#10B981;box-shadow:0 0 6px #10B981;"></span>● LIVE SESSION TODAY</span>`;
|
|
978
|
+
} else {
|
|
979
|
+
liveBadge = `<span style="background:rgba(148,163,184,0.1);color:#94A3B8;border:1px solid rgba(148,163,184,0.2);padding:2px 8px;border-radius:12px;font-size:10px;font-weight:500;display:inline-flex;align-items:center;gap:4px;margin-left:8px;"><span style="width:6px;height:6px;border-radius:50%;background:#64748B;"></span>Idle</span>`;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
if (f.agentKey) {
|
|
983
|
+
const agentConvLogs = logs.filter(l => l.command && l.command.includes(`conv:${f.agentKey}`));
|
|
984
|
+
const lastLog = agentConvLogs.length > 0 ? agentConvLogs[agentConvLogs.length - 1] : null;
|
|
985
|
+
const savedTokens = (sess && sess.activeSavedTokens > 0) ? sess.activeSavedTokens : (lastLog ? (lastLog.tokens_saved || lastLog.estimated_raw_tokens || 0) : 0);
|
|
986
|
+
if (savedTokens > 0 || (lastLog && lastLog.timestamp)) {
|
|
987
|
+
const timeStr = lastLog && lastLog.timestamp ? new Date(lastLog.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : (sess && sess.lastModified ? new Date(sess.lastModified).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : 'Today');
|
|
988
|
+
claudeCompactHtml = `<div style="font-size:11px;color:#F59E0B;margin-top:4px;font-family:sans-serif;display:inline-flex;align-items:center;gap:6px;font-weight:600;background:rgba(245,158,11,0.12);border:1px solid rgba(245,158,11,0.3);padding:2px 8px;border-radius:6px;"><span style="display:inline-block;width:6px;height:6px;border-radius:50%;background:#F59E0B;box-shadow:0 0 6px #F59E0B;"></span>🟡 Last Compacted: ${timeStr} (${savedTokens.toLocaleString()} tokens)</div>`;
|
|
989
|
+
} else {
|
|
990
|
+
claudeCompactHtml = `<div style="font-size:11px;color:#F59E0B;margin-top:4px;font-family:sans-serif;display:inline-flex;align-items:center;gap:6px;font-weight:600;background:rgba(245,158,11,0.12);border:1px solid rgba(245,158,11,0.3);padding:2px 8px;border-radius:6px;"><span style="display:inline-block;width:6px;height:6px;border-radius:50%;background:#F59E0B;box-shadow:0 0 6px #F59E0B;"></span>🟡 Last Compacted: Active Monitoring</div>`;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
if (sess && sess.fileName) {
|
|
995
|
+
latestFileHtml = `<div style="font-size:11px;color:#10B981;margin-top:3px;font-family:monospace;display:flex;align-items:center;gap:4px;"><span>📄 Latest Session:</span> <strong style="color:#F1F5F9;">${sess.fileName}</strong></div>`;
|
|
996
|
+
} else {
|
|
997
|
+
latestFileHtml = `<div style="font-size:11px;color:#64748B;margin-top:3px;font-family:monospace;"><span>📄 Waiting for active session file...</span></div>`;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
921
1001
|
html += `<div class="${rowClass}">
|
|
922
1002
|
<label class="csa-toggle">
|
|
923
1003
|
<input type="checkbox" ${checked ? 'checked' : ''} ${isSoon ? 'disabled' : ''} onchange="saveCsaToggle('${f.key}', this.checked)">
|
|
924
1004
|
<span class="slider"></span>
|
|
925
1005
|
</label>
|
|
926
1006
|
<div class="csa-info">
|
|
927
|
-
<h4>${f.title}
|
|
1007
|
+
<h4>${f.title} ${liveBadge}</h4>
|
|
928
1008
|
<p>${f.desc}</p>
|
|
929
|
-
|
|
1009
|
+
${latestFileHtml}
|
|
1010
|
+
${claudeCompactHtml}
|
|
1011
|
+
<div class="csa-badges" style="margin-top:6px;">
|
|
1012
|
+
${f.badges.map(b => `<span class="csa-badge">${b}</span>`).join('')}
|
|
1013
|
+
${f.agentKey ? `<button type="button" style="background:rgba(245,158,11,0.15);border:1px solid rgba(245,158,11,0.4);color:#F59E0B;font-size:10px;padding:2px 8px;border-radius:4px;cursor:pointer;margin-left:6px;font-weight:600;" onclick="event.preventDefault(); runManualCompact('${f.agentKey}', this)">⚡ Compact Now</button>` : ''}
|
|
1014
|
+
${f.agentKey ? `<button style="background:rgba(16,185,129,0.12);border:1px solid rgba(16,185,129,0.3);color:#10B981;font-size:10px;padding:2px 8px;border-radius:4px;cursor:pointer;margin-left:4px;font-weight:600;" onclick="openAgentFolder('${f.agentKey}')">📁 Open Directory</button>` : ''}
|
|
1015
|
+
${f.agentKey ? `<button style="background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.12);color:#94A3B8;font-size:10px;padding:2px 8px;border-radius:4px;cursor:pointer;margin-left:4px;" onclick="openLocateModal('${f.agentKey}', '${f.title}')">⚙️ Change Path</button>` : ''}
|
|
1016
|
+
</div>
|
|
930
1017
|
</div>
|
|
931
1018
|
<div class="csa-stat">
|
|
932
1019
|
<div class="csa-val" style="color:${statColor};">${statVal}</div>
|
|
933
|
-
<div class="csa-sub">${
|
|
1020
|
+
<div class="csa-sub">${statSubText}</div>
|
|
934
1021
|
</div>
|
|
935
1022
|
</div>`;
|
|
936
1023
|
});
|
|
@@ -962,14 +1049,87 @@
|
|
|
962
1049
|
<div class="csa-sum-label">Secrets Blocked</div>
|
|
963
1050
|
<div class="csa-sum-val" style="color:var(--red);">${totalSecrets.toLocaleString()}</div>
|
|
964
1051
|
</div>`;
|
|
1052
|
+
} catch(err) {
|
|
1053
|
+
console.error('Error rendering CSA section:', err);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
async function openLocateModal(agentKey, title) {
|
|
1058
|
+
let currentPath = 'Default system path';
|
|
1059
|
+
try {
|
|
1060
|
+
const resp = await fetch('/api/config');
|
|
1061
|
+
const cfg = await resp.json();
|
|
1062
|
+
if (cfg.resolvedPaths && cfg.resolvedPaths[agentKey]) {
|
|
1063
|
+
currentPath = cfg.resolvedPaths[agentKey].join(' ; ');
|
|
1064
|
+
}
|
|
1065
|
+
if (cfg.custom_storage_paths && cfg.custom_storage_paths[agentKey]) {
|
|
1066
|
+
currentPath = cfg.custom_storage_paths[agentKey];
|
|
1067
|
+
}
|
|
1068
|
+
} catch(e){}
|
|
1069
|
+
|
|
1070
|
+
const customPath = prompt(`⚙️ Configure Session Storage Directory for ${title}:\n\nCurrently Monitored Path:\n${currentPath}\n\nEnter custom directory path (or leave empty to reset to default):`, currentPath.includes(' ; ') ? '' : currentPath);
|
|
1071
|
+
if (customPath !== null) {
|
|
1072
|
+
try {
|
|
1073
|
+
await fetch('/api/config/custom-path', {
|
|
1074
|
+
method: 'POST',
|
|
1075
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1076
|
+
body: JSON.stringify({ agentKey, path: customPath.trim() || null })
|
|
1077
|
+
});
|
|
1078
|
+
alert(`Storage path updated for ${title}!\nMonitored directory is now active and saved permanently.`);
|
|
1079
|
+
renderCsa();
|
|
1080
|
+
} catch (e) {
|
|
1081
|
+
alert('Failed to save path.');
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
async function openAgentFolder(agentKey) {
|
|
1087
|
+
try {
|
|
1088
|
+
const resp = await fetch('/api/config/open-dir', {
|
|
1089
|
+
method: 'POST',
|
|
1090
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1091
|
+
body: JSON.stringify({ agentKey })
|
|
1092
|
+
});
|
|
1093
|
+
const res = await resp.json();
|
|
1094
|
+
if (!res.success) {
|
|
1095
|
+
alert('Could not open directory.');
|
|
1096
|
+
}
|
|
1097
|
+
} catch (e) {
|
|
1098
|
+
alert('Error opening directory.');
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
async function runManualCompact(agentKey, btn) {
|
|
1103
|
+
const origText = btn.innerText;
|
|
1104
|
+
btn.innerText = '⏳ Compacting...';
|
|
1105
|
+
btn.disabled = true;
|
|
1106
|
+
try {
|
|
1107
|
+
const resp = await fetch('/api/agent/compact', {
|
|
1108
|
+
method: 'POST',
|
|
1109
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1110
|
+
body: JSON.stringify({ agentKey })
|
|
1111
|
+
});
|
|
1112
|
+
const res = await resp.json();
|
|
1113
|
+
if (res.ok) {
|
|
1114
|
+
btn.innerText = '✅ Compacted!';
|
|
1115
|
+
setTimeout(() => { btn.innerText = origText; btn.disabled = false; }, 2000);
|
|
1116
|
+
loadStats();
|
|
1117
|
+
} else {
|
|
1118
|
+
alert(res.message || 'Compaction finished');
|
|
1119
|
+
btn.innerText = origText;
|
|
1120
|
+
btn.disabled = false;
|
|
1121
|
+
}
|
|
1122
|
+
} catch(e) {
|
|
1123
|
+
btn.innerText = origText;
|
|
1124
|
+
btn.disabled = false;
|
|
1125
|
+
}
|
|
965
1126
|
}
|
|
966
1127
|
|
|
967
1128
|
function isAiCommand(log) {
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
return true;
|
|
1129
|
+
if (!log) return false;
|
|
1130
|
+
const cmd = log.command || '';
|
|
1131
|
+
const feats = log.features || [];
|
|
1132
|
+
return cmd.startsWith('conv:') || feats.includes('live-conv') || feats.some(f => typeof f === 'string' && f.startsWith('conv_'));
|
|
973
1133
|
}
|
|
974
1134
|
|
|
975
1135
|
function setFilter(mode) {
|
|
@@ -1122,39 +1282,21 @@
|
|
|
1122
1282
|
|
|
1123
1283
|
async function loadStats() {
|
|
1124
1284
|
const select = document.getElementById('model-select');
|
|
1125
|
-
const model = select.value;
|
|
1285
|
+
const model = select ? select.value : 'auto';
|
|
1126
1286
|
|
|
1127
1287
|
try {
|
|
1128
|
-
// Fetch full history and compute AI-only stats client-side
|
|
1129
|
-
const histResp = await fetch('/api/stats/history');
|
|
1130
|
-
const allLogs = await histResp.json();
|
|
1131
|
-
const aiLogs = allLogs.filter(l => isAiCommand(l));
|
|
1132
|
-
|
|
1133
1288
|
const summaryResp = await fetch(`/api/stats/summary?model=${model}`);
|
|
1289
|
+
if (!summaryResp.ok) return;
|
|
1134
1290
|
const data = await summaryResp.json();
|
|
1135
1291
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
}
|
|
1144
|
-
const pct = totalRaw > 0 ? ((totalSaved / totalRaw) * 100).toFixed(1) : '0.0';
|
|
1145
|
-
const rawCostUsd = (totalRaw * pricePerM) / 1000000;
|
|
1146
|
-
const compCostUsd = (totalComp * pricePerM) / 1000000;
|
|
1147
|
-
const savedUsd = (totalSaved * pricePerM) / 1000000;
|
|
1148
|
-
|
|
1149
|
-
document.getElementById('val-savings').innerText = `$${savedUsd.toFixed(4)}`;
|
|
1150
|
-
document.getElementById('val-savings-desc').innerText = `based on ${data.model_name} pricing`;
|
|
1151
|
-
document.getElementById('val-orig-cost').innerText = `$${rawCostUsd.toFixed(4)}`;
|
|
1152
|
-
document.getElementById('val-comp-cost').innerText = `$${compCostUsd.toFixed(4)}`;
|
|
1153
|
-
document.getElementById('val-reduction').innerText = `${pct}%`;
|
|
1154
|
-
document.getElementById('val-tokens-desc').innerText = `raw: ${totalRaw.toLocaleString()} → compressed: ${totalComp.toLocaleString()}`;
|
|
1155
|
-
|
|
1156
|
-
// Return for chart model resolution
|
|
1157
|
-
data.model_key = data.model_key || model;
|
|
1292
|
+
if (data && data.raw_cost_usd !== undefined) {
|
|
1293
|
+
document.getElementById('val-savings').innerText = `$${data.money_saved_usd.toFixed(4)}`;
|
|
1294
|
+
document.getElementById('val-savings-desc').innerText = `based on ${data.model_name} pricing`;
|
|
1295
|
+
document.getElementById('val-orig-cost').innerText = `$${data.raw_cost_usd.toFixed(4)}`;
|
|
1296
|
+
document.getElementById('val-comp-cost').innerText = `$${data.compressed_cost_usd.toFixed(4)}`;
|
|
1297
|
+
document.getElementById('val-reduction').innerText = `${data.savings_percentage}%`;
|
|
1298
|
+
document.getElementById('val-tokens-desc').innerText = `raw: ${data.total_raw_tokens.toLocaleString()} → compressed: ${data.total_compressed_tokens.toLocaleString()}`;
|
|
1299
|
+
}
|
|
1158
1300
|
return data;
|
|
1159
1301
|
} catch (err) {
|
|
1160
1302
|
console.error('Failed to fetch summary stats', err);
|
|
@@ -1163,27 +1305,25 @@
|
|
|
1163
1305
|
|
|
1164
1306
|
async function loadHistory() {
|
|
1165
1307
|
const select = document.getElementById('model-select');
|
|
1166
|
-
const model = select.value;
|
|
1308
|
+
const model = select ? select.value : 'auto';
|
|
1309
|
+
|
|
1310
|
+
loadStats();
|
|
1167
1311
|
|
|
1168
1312
|
try {
|
|
1169
1313
|
const response = await fetch('/api/stats/history');
|
|
1314
|
+
if (!response.ok) return;
|
|
1170
1315
|
const logs = await response.json();
|
|
1316
|
+
if (!Array.isArray(logs)) return;
|
|
1171
1317
|
|
|
1172
|
-
|
|
1173
|
-
if (model === 'auto') {
|
|
1174
|
-
const stats = await loadStats();
|
|
1175
|
-
if (stats && stats.model_key) {
|
|
1176
|
-
resolvedModel = stats.model_key;
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1318
|
+
const resolvedModel = model === 'auto' ? 'claude-opus-4-8' : model;
|
|
1179
1319
|
|
|
1180
|
-
// Filter logs: AI-only for chart
|
|
1320
|
+
// Filter logs: AI-only for chart
|
|
1181
1321
|
const aiLogs = logs.filter(l => isAiCommand(l));
|
|
1182
1322
|
|
|
1183
|
-
// Chart
|
|
1323
|
+
// Chart always uses AI-only logs
|
|
1184
1324
|
renderChart(aiLogs, resolvedModel);
|
|
1185
1325
|
|
|
1186
|
-
// CSA Feature Controls
|
|
1326
|
+
// CSA Feature Controls (Run asynchronously in background)
|
|
1187
1327
|
renderCsaSection(logs);
|
|
1188
1328
|
|
|
1189
1329
|
// Security banner — aggregate across all logs
|
|
@@ -1222,53 +1362,118 @@
|
|
|
1222
1362
|
return;
|
|
1223
1363
|
}
|
|
1224
1364
|
|
|
1225
|
-
|
|
1226
|
-
const
|
|
1365
|
+
// Group logs by command name
|
|
1366
|
+
const groups = {};
|
|
1367
|
+
displayLogs.forEach(l => {
|
|
1368
|
+
const key = l.command || 'unknown';
|
|
1369
|
+
if (!groups[key]) groups[key] = [];
|
|
1370
|
+
groups[key].push(l);
|
|
1371
|
+
});
|
|
1372
|
+
|
|
1373
|
+
// Sort groups by most recent log timestamp descending
|
|
1374
|
+
const sortedGroupKeys = Object.keys(groups).sort((a, b) => {
|
|
1375
|
+
const lastA = new Date(groups[a][groups[a].length - 1].timestamp).getTime();
|
|
1376
|
+
const lastB = new Date(groups[b][groups[b].length - 1].timestamp).getTime();
|
|
1377
|
+
return lastB - lastA;
|
|
1378
|
+
});
|
|
1379
|
+
|
|
1227
1380
|
tbody.innerHTML = '';
|
|
1228
1381
|
|
|
1229
|
-
|
|
1230
|
-
const
|
|
1231
|
-
const
|
|
1232
|
-
const
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1382
|
+
sortedGroupKeys.forEach((cmdKey, gIdx) => {
|
|
1383
|
+
const groupLogs = groups[cmdKey].reverse(); // Most recent first
|
|
1384
|
+
const latestLog = groupLogs[0];
|
|
1385
|
+
const latestTime = new Date(latestLog.timestamp).toLocaleTimeString();
|
|
1386
|
+
|
|
1387
|
+
let grpRaw = 0, grpComp = 0, grpSaved = 0, grpSecrets = 0;
|
|
1388
|
+
const isLiveConvGroup = cmdKey.startsWith('conv:') || (latestLog.features && latestLog.features.includes('live-conv'));
|
|
1389
|
+
if (isLiveConvGroup) {
|
|
1390
|
+
grpRaw = latestLog.estimated_raw_tokens || 0;
|
|
1391
|
+
grpComp = latestLog.estimated_compressed_tokens || 0;
|
|
1392
|
+
grpSaved = grpRaw - grpComp;
|
|
1393
|
+
groupLogs.forEach(l => { grpSecrets += (l.secrets_found || 0); });
|
|
1394
|
+
} else {
|
|
1395
|
+
groupLogs.forEach(l => {
|
|
1396
|
+
grpRaw += (l.estimated_raw_tokens || 0);
|
|
1397
|
+
grpComp += (l.estimated_compressed_tokens || 0);
|
|
1398
|
+
grpSaved += (l.tokens_saved || 0);
|
|
1399
|
+
grpSecrets += (l.secrets_found || 0);
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
const grpSavingsPct = grpRaw > 0 ? ((grpSaved / grpRaw) * 100).toFixed(1) : '0.0';
|
|
1403
|
+
const groupId = `grp-${gIdx}`;
|
|
1404
|
+
const isGroupExpanded = expandedGroupKeys.has(cmdKey);
|
|
1405
|
+
|
|
1406
|
+
const trHead = document.createElement('tr');
|
|
1407
|
+
trHead.style.background = 'rgba(255,255,255,0.03)';
|
|
1408
|
+
trHead.style.cursor = 'pointer';
|
|
1409
|
+
trHead.style.borderBottom = '1px solid rgba(255,255,255,0.06)';
|
|
1410
|
+
trHead.onclick = () => {
|
|
1411
|
+
const el = document.querySelectorAll(`.${groupId}`);
|
|
1412
|
+
const chev = document.getElementById(`chev-${groupId}`);
|
|
1413
|
+
const isHidden = el[0] && el[0].style.display === 'none';
|
|
1414
|
+
if (isHidden) {
|
|
1415
|
+
expandedGroupKeys.add(cmdKey);
|
|
1416
|
+
} else {
|
|
1417
|
+
expandedGroupKeys.delete(cmdKey);
|
|
1418
|
+
}
|
|
1419
|
+
el.forEach(row => row.style.display = isHidden ? '' : 'none');
|
|
1420
|
+
if (chev) chev.style.transform = isHidden ? 'rotate(90deg)' : 'rotate(0deg)';
|
|
1421
|
+
};
|
|
1422
|
+
|
|
1423
|
+
const displayCmd = formatCommandName(latestLog.command);
|
|
1424
|
+
|
|
1425
|
+
trHead.innerHTML = `
|
|
1426
|
+
<td style="font-size:12px;color:#94A3B8;white-space:nowrap;"><span id="chev-${groupId}" style="display:inline-block;transition:transform 0.2s;margin-right:6px;font-size:10px;transform:${isGroupExpanded ? 'rotate(90deg)' : 'rotate(0deg)'}">▶</span>${latestTime}</td>
|
|
1427
|
+
<td style="font-weight: 600;color:#F1F5F9;text-transform:capitalize;">${displayCmd} <span style="font-size:11px;color:#818CF8;font-weight:400;margin-left:6px;">(${groupLogs.length} runs)</span>${grpSecrets ? ' <span style="color:#F09595;font-size:11px;" title="' + grpSecrets + ' secrets redacted">🛡' + grpSecrets + '</span>' : ''}</td>
|
|
1428
|
+
<td>${featureTagHtml(latestLog.features)}</td>
|
|
1429
|
+
<td style="font-weight:500;">${grpRaw.toLocaleString()}</td>
|
|
1430
|
+
<td style="font-weight:500;">${grpComp.toLocaleString()}</td>
|
|
1431
|
+
<td style="color: var(--emerald); font-weight: 700;">${grpSavingsPct}%</td>
|
|
1432
|
+
<td><span class="status-badge success">Active</span></td>
|
|
1433
|
+
<td><button class="btn-inspect" style="background:rgba(129,140,248,0.15);color:#818CF8;border:1px solid rgba(129,140,248,0.3);" onclick="event.stopPropagation(); this.closest('tr').click();">Details (${groupLogs.length})</button></td>
|
|
1250
1434
|
`;
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1435
|
+
tbody.appendChild(trHead);
|
|
1436
|
+
|
|
1437
|
+
// Append child rows
|
|
1438
|
+
groupLogs.forEach(log => {
|
|
1439
|
+
const tr = document.createElement('tr');
|
|
1440
|
+
tr.className = groupId;
|
|
1441
|
+
tr.style.display = isGroupExpanded ? '' : 'none';
|
|
1442
|
+
tr.style.background = 'rgba(0,0,0,0.2)';
|
|
1443
|
+
const time = new Date(log.timestamp).toLocaleTimeString();
|
|
1444
|
+
const savingsPct = log.raw_chars > 0
|
|
1445
|
+
? ((log.estimated_raw_tokens - log.estimated_compressed_tokens) / log.estimated_raw_tokens * 100).toFixed(1)
|
|
1446
|
+
: '0.0';
|
|
1447
|
+
const isConvLog = log.command && log.command.includes('conv:');
|
|
1448
|
+
let convBadgeHtml = '';
|
|
1449
|
+
if (log.features && log.features.includes('trim_offload')) {
|
|
1450
|
+
convBadgeHtml = `<div style="font-size:10px;color:#10B981;margin-top:4px;padding:2px 8px;border-radius:4px;background:rgba(16,185,129,0.1);display:inline-block;font-weight:600;">⚡ TrimPrompt Side-Cache Offloaded ${log.estimated_raw_tokens.toLocaleString()} Tokens</div>`;
|
|
1451
|
+
} else if (log.features && log.features.includes('official_compact')) {
|
|
1452
|
+
convBadgeHtml = `<div style="font-size:10px;color:#818CF8;margin-top:4px;padding:2px 8px;border-radius:4px;background:rgba(129,140,248,0.1);display:inline-block;font-weight:600;">📦 Official Agent Compact Saved ${log.estimated_raw_tokens.toLocaleString()} Tokens</div>`;
|
|
1453
|
+
} else if (isConvLog) {
|
|
1454
|
+
convBadgeHtml = `<div style="font-size:10px;color:#F59E0B;margin-top:4px;padding:2px 8px;border-radius:4px;background:rgba(245,158,11,0.1);display:inline-block;font-weight:500;">🔄 Active Session Context Monitored</div>`;
|
|
1455
|
+
}
|
|
1456
|
+
const childCmd = formatCommandName(log.command);
|
|
1457
|
+
|
|
1458
|
+
tr.innerHTML = `
|
|
1459
|
+
<td style="padding-left:24px;font-size:11px;color:#64748B;white-space:nowrap;">↳ ${time}</td>
|
|
1460
|
+
<td style="font-size:12px;color:#CBD5E1;text-transform:capitalize;">${childCmd}${convBadgeHtml}</td>
|
|
1461
|
+
<td>${featureTagHtml(log.features)}</td>
|
|
1462
|
+
<td style="font-size:12px;">${log.estimated_raw_tokens.toLocaleString()}</td>
|
|
1463
|
+
<td style="font-size:12px;">${log.estimated_compressed_tokens.toLocaleString()}</td>
|
|
1464
|
+
<td style="color: var(--emerald); font-size:12px;">${savingsPct}%</td>
|
|
1465
|
+
<td>
|
|
1466
|
+
<span class="status-badge ${log.exit_code === 0 ? 'success' : 'fail'}">
|
|
1467
|
+
${log.exit_code}
|
|
1468
|
+
</span>
|
|
1469
|
+
</td>
|
|
1470
|
+
<td>
|
|
1471
|
+
<button class="btn-inspect" data-id="${log.id}" data-cmd="${log.command}" data-sec="${log.secrets_found || 0}" data-types="${(log.secret_types || []).join(',')}" onclick="inspectFromBtn(this)">Inspect</button>
|
|
1472
|
+
</td>
|
|
1473
|
+
`;
|
|
1269
1474
|
tbody.appendChild(tr);
|
|
1270
1475
|
});
|
|
1271
|
-
}
|
|
1476
|
+
});
|
|
1272
1477
|
} catch (err) {
|
|
1273
1478
|
console.error('Failed to fetch history logs', err);
|
|
1274
1479
|
}
|
|
@@ -1278,6 +1483,15 @@
|
|
|
1278
1483
|
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1279
1484
|
}
|
|
1280
1485
|
|
|
1486
|
+
function formatCommandName(cmd) {
|
|
1487
|
+
if (!cmd) return 'unknown';
|
|
1488
|
+
if (cmd.includes('conv:')) {
|
|
1489
|
+
const parts = cmd.split('conv:');
|
|
1490
|
+
return parts[1] || cmd;
|
|
1491
|
+
}
|
|
1492
|
+
return cmd;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1281
1495
|
// Human-friendly labels for redactor rule types (see redactor.ts)
|
|
1282
1496
|
const SECRET_TYPE_LABELS = {
|
|
1283
1497
|
aws_key: 'AWS access key',
|
|
@@ -1354,7 +1568,8 @@
|
|
|
1354
1568
|
|
|
1355
1569
|
async function inspectDiff(id, command, secretsFound, secretTypes) {
|
|
1356
1570
|
try {
|
|
1357
|
-
|
|
1571
|
+
const cleanCmd = formatCommandName(command);
|
|
1572
|
+
document.getElementById('modal-command-title').innerText = `Inspector: trim ${cleanCmd}`;
|
|
1358
1573
|
document.getElementById('diff-raw').innerText = 'Loading...';
|
|
1359
1574
|
document.getElementById('diff-comp').innerText = 'Loading...';
|
|
1360
1575
|
document.getElementById('diff-modal').style.display = 'flex';
|
|
@@ -1362,8 +1577,13 @@
|
|
|
1362
1577
|
const response = await fetch(`/api/stats/inspect/${id}`);
|
|
1363
1578
|
const data = await response.json();
|
|
1364
1579
|
|
|
1365
|
-
|
|
1366
|
-
|
|
1580
|
+
if (command && command.includes('conv:')) {
|
|
1581
|
+
document.getElementById('diff-raw').innerHTML = `<div style="color:#F59E0B;font-size:13px;padding:24px;font-weight:600;line-height:1.6;">⚡ Live Conversation Context Monitor Active<br><span style="font-size:12px;color:#94A3B8;font-weight:400;">Savings achieved via conversation compaction (Native Hook & Side-Cache active)</span></div>`;
|
|
1582
|
+
document.getElementById('diff-comp').innerHTML = `<div style="color:#10B981;font-size:13px;padding:24px;font-weight:600;line-height:1.6;">✅ Tokens Trimmed & Offloaded Live<br><span style="font-size:12px;color:#94A3B8;font-weight:400;">Active memory trimming and real-time token savings within conversation</span></div>`;
|
|
1583
|
+
} else {
|
|
1584
|
+
document.getElementById('diff-raw').innerHTML = highlightRedacted(escapeHtml(data.raw), 'red');
|
|
1585
|
+
document.getElementById('diff-comp').innerHTML = highlightRedacted(escapeHtml(data.compressed), 'green');
|
|
1586
|
+
}
|
|
1367
1587
|
|
|
1368
1588
|
const info = detectFilterType(data.raw, data.compressed, command);
|
|
1369
1589
|
const bar = document.getElementById('inspect-info-bar');
|