nuxs-capsule 0.1.1
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 +21 -0
- package/bin/nuxs-capsule-intercept.js +4 -0
- package/bin/nuxs-capsule.js +20 -0
- package/dist/install.d.ts +17 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +243 -0
- package/dist/install.js.map +1 -0
- package/dist/intercept.d.ts +19 -0
- package/dist/intercept.d.ts.map +1 -0
- package/dist/intercept.js +232 -0
- package/dist/intercept.js.map +1 -0
- package/dist/lib/fileStat.d.ts +20 -0
- package/dist/lib/fileStat.d.ts.map +1 -0
- package/dist/lib/fileStat.js +71 -0
- package/dist/lib/fileStat.js.map +1 -0
- package/dist/lib/fingerprint.d.ts +10 -0
- package/dist/lib/fingerprint.d.ts.map +1 -0
- package/dist/lib/fingerprint.js +41 -0
- package/dist/lib/fingerprint.js.map +1 -0
- package/dist/lib/tokenCount.d.ts +13 -0
- package/dist/lib/tokenCount.d.ts.map +1 -0
- package/dist/lib/tokenCount.js +32 -0
- package/dist/lib/tokenCount.js.map +1 -0
- package/dist/license/check.d.ts +14 -0
- package/dist/license/check.d.ts.map +1 -0
- package/dist/license/check.js +141 -0
- package/dist/license/check.js.map +1 -0
- package/dist/providers/llmClient.d.ts +20 -0
- package/dist/providers/llmClient.d.ts.map +1 -0
- package/dist/providers/llmClient.js +40 -0
- package/dist/providers/llmClient.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +268 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +14 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +135 -0
- package/dist/setup.js.map +1 -0
- package/dist/telemetry/usage.d.ts +19 -0
- package/dist/telemetry/usage.d.ts.map +1 -0
- package/dist/telemetry/usage.js +114 -0
- package/dist/telemetry/usage.js.map +1 -0
- package/dist/tools/apiCapsule.d.ts +16 -0
- package/dist/tools/apiCapsule.d.ts.map +1 -0
- package/dist/tools/apiCapsule.js +169 -0
- package/dist/tools/apiCapsule.js.map +1 -0
- package/dist/tools/eventsCapsule.d.ts +15 -0
- package/dist/tools/eventsCapsule.d.ts.map +1 -0
- package/dist/tools/eventsCapsule.js +72 -0
- package/dist/tools/eventsCapsule.js.map +1 -0
- package/dist/tools/logCapsule.d.ts +38 -0
- package/dist/tools/logCapsule.d.ts.map +1 -0
- package/dist/tools/logCapsule.js +283 -0
- package/dist/tools/logCapsule.js.map +1 -0
- package/dist/tools/networkCapsule.d.ts +18 -0
- package/dist/tools/networkCapsule.d.ts.map +1 -0
- package/dist/tools/networkCapsule.js +151 -0
- package/dist/tools/networkCapsule.js.map +1 -0
- package/dist/tools/sessionCapsule.d.ts +21 -0
- package/dist/tools/sessionCapsule.d.ts.map +1 -0
- package/dist/tools/sessionCapsule.js +174 -0
- package/dist/tools/sessionCapsule.js.map +1 -0
- package/dist/tools/sqlCapsule.d.ts +17 -0
- package/dist/tools/sqlCapsule.d.ts.map +1 -0
- package/dist/tools/sqlCapsule.js +144 -0
- package/dist/tools/sqlCapsule.js.map +1 -0
- package/dist/tools/stackCapsule.d.ts +9 -0
- package/dist/tools/stackCapsule.d.ts.map +1 -0
- package/dist/tools/stackCapsule.js +132 -0
- package/dist/tools/stackCapsule.js.map +1 -0
- package/dist/tools/threadsCapsule.d.ts +14 -0
- package/dist/tools/threadsCapsule.d.ts.map +1 -0
- package/dist/tools/threadsCapsule.js +77 -0
- package/dist/tools/threadsCapsule.js.map +1 -0
- package/dist/types.d.ts +199 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/wrap.d.ts +14 -0
- package/dist/wrap.d.ts.map +1 -0
- package/dist/wrap.js +100 -0
- package/dist/wrap.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* network_capsule — comprime HAR (HTTP Archive) do DevTools.
|
|
3
|
+
* Algoritmo puro, zero LLM, zero custo recorrente.
|
|
4
|
+
*
|
|
5
|
+
* Compressão típica: 50-100k tokens → 1-2k tokens (50-66×).
|
|
6
|
+
*
|
|
7
|
+
* Mecânica:
|
|
8
|
+
* 1. Parse JSON HAR
|
|
9
|
+
* 2. Sort por tempo total → top 10 slowest
|
|
10
|
+
* 3. Filter erros (4xx, 5xx, connection refused)
|
|
11
|
+
* 4. Agrega por host (count, total_ms, avg_ms)
|
|
12
|
+
* 5. Critical path — waterfall causal (req B começou depois de A terminar)
|
|
13
|
+
* 6. Blocking — reqs com timings.blocked > 100ms
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
export function networkCapsuleFromFile(filePath) {
|
|
17
|
+
const raw = readFileSync(filePath, 'utf8');
|
|
18
|
+
return networkCapsule(raw);
|
|
19
|
+
}
|
|
20
|
+
export function networkCapsule(harJson) {
|
|
21
|
+
const har = JSON.parse(harJson);
|
|
22
|
+
const entries = har.log.entries;
|
|
23
|
+
// 1. Slowest 10
|
|
24
|
+
const sorted = [...entries].sort((a, b) => b.time - a.time);
|
|
25
|
+
const slowest = sorted.slice(0, 10).map((e) => ({
|
|
26
|
+
url: truncateUrl(e.request.url),
|
|
27
|
+
method: e.request.method,
|
|
28
|
+
duration_ms: Math.round(e.time),
|
|
29
|
+
status: e.response.status
|
|
30
|
+
}));
|
|
31
|
+
// 2. Errors
|
|
32
|
+
const errors = entries
|
|
33
|
+
.filter((e) => e.response.status >= 400 || e.response._error)
|
|
34
|
+
.slice(0, 20)
|
|
35
|
+
.map((e) => ({
|
|
36
|
+
url: truncateUrl(e.request.url),
|
|
37
|
+
status: e.response.status,
|
|
38
|
+
error: e.response._error ?? statusReason(e.response.status)
|
|
39
|
+
}));
|
|
40
|
+
// 3. By host
|
|
41
|
+
const byHost = {};
|
|
42
|
+
for (const e of entries) {
|
|
43
|
+
const host = safeHostname(e.request.url);
|
|
44
|
+
if (!byHost[host])
|
|
45
|
+
byHost[host] = { count: 0, total_ms: 0, avg_ms: 0 };
|
|
46
|
+
byHost[host].count++;
|
|
47
|
+
byHost[host].total_ms += e.time;
|
|
48
|
+
}
|
|
49
|
+
for (const h of Object.values(byHost)) {
|
|
50
|
+
h.avg_ms = Math.round(h.total_ms / h.count);
|
|
51
|
+
h.total_ms = Math.round(h.total_ms);
|
|
52
|
+
}
|
|
53
|
+
// 4. Critical path — sequência causal
|
|
54
|
+
const critical_path = computeCriticalPath(entries);
|
|
55
|
+
// 5. Blocking
|
|
56
|
+
const blocking = entries
|
|
57
|
+
.filter((e) => (e.timings.blocked ?? 0) > 100)
|
|
58
|
+
.slice(0, 10)
|
|
59
|
+
.map((e) => ({
|
|
60
|
+
url: truncateUrl(e.request.url),
|
|
61
|
+
blocked_by: detectBlocker(e, entries),
|
|
62
|
+
wait_ms: Math.round(e.timings.blocked)
|
|
63
|
+
}));
|
|
64
|
+
return {
|
|
65
|
+
total_requests: entries.length,
|
|
66
|
+
total_time_ms: Math.round(har.log.pages?.[0]?.pageTimings?.onLoad ?? sumTime(entries)),
|
|
67
|
+
slowest,
|
|
68
|
+
errors,
|
|
69
|
+
by_host: byHost,
|
|
70
|
+
critical_path,
|
|
71
|
+
blocking
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function truncateUrl(url) {
|
|
75
|
+
// URLs absurdamente longas (query strings gigantes) — corta com elipse
|
|
76
|
+
if (url.length <= 120)
|
|
77
|
+
return url;
|
|
78
|
+
return url.slice(0, 117) + '...';
|
|
79
|
+
}
|
|
80
|
+
function safeHostname(url) {
|
|
81
|
+
try {
|
|
82
|
+
return new URL(url).hostname;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return 'invalid';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function statusReason(status) {
|
|
89
|
+
if (status >= 500)
|
|
90
|
+
return 'server_error';
|
|
91
|
+
if (status >= 400)
|
|
92
|
+
return 'client_error';
|
|
93
|
+
return '';
|
|
94
|
+
}
|
|
95
|
+
function sumTime(entries) {
|
|
96
|
+
return entries.reduce((acc, e) => acc + e.time, 0);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Critical path = sequência de requests que dominam o tempo total.
|
|
100
|
+
* Heurística: ordena por startedDateTime, identifica chains de requests
|
|
101
|
+
* que iniciam logo após o término de outro (gap < 50ms).
|
|
102
|
+
*/
|
|
103
|
+
function computeCriticalPath(entries) {
|
|
104
|
+
if (entries.length === 0)
|
|
105
|
+
return [];
|
|
106
|
+
const sorted = [...entries].sort((a, b) => new Date(a.startedDateTime).getTime() - new Date(b.startedDateTime).getTime());
|
|
107
|
+
// Pega o request mais demorado em cada "fase" da página (chunks de tempo)
|
|
108
|
+
const totalDuration = new Date(sorted[sorted.length - 1].startedDateTime).getTime() +
|
|
109
|
+
sorted[sorted.length - 1].time -
|
|
110
|
+
new Date(sorted[0].startedDateTime).getTime();
|
|
111
|
+
const numPhases = Math.min(8, Math.max(3, Math.ceil(totalDuration / 1000)));
|
|
112
|
+
const phaseSize = totalDuration / numPhases;
|
|
113
|
+
const startTs = new Date(sorted[0].startedDateTime).getTime();
|
|
114
|
+
const phases = Array.from({ length: numPhases }, () => []);
|
|
115
|
+
for (const e of sorted) {
|
|
116
|
+
const offset = new Date(e.startedDateTime).getTime() - startTs;
|
|
117
|
+
const idx = Math.min(numPhases - 1, Math.floor(offset / phaseSize));
|
|
118
|
+
phases[idx].push(e);
|
|
119
|
+
}
|
|
120
|
+
const path = [];
|
|
121
|
+
for (const phase of phases) {
|
|
122
|
+
if (phase.length === 0)
|
|
123
|
+
continue;
|
|
124
|
+
const heaviest = phase.reduce((max, e) => (e.time > max.time ? e : max));
|
|
125
|
+
path.push(`${heaviest.request.method} ${truncateUrl(heaviest.request.url)} (${Math.round(heaviest.time)}ms)`);
|
|
126
|
+
}
|
|
127
|
+
return path;
|
|
128
|
+
}
|
|
129
|
+
/** Quem provavelmente bloqueou — request anterior do mesmo host. */
|
|
130
|
+
function detectBlocker(entry, entries) {
|
|
131
|
+
const host = safeHostname(entry.request.url);
|
|
132
|
+
const start = new Date(entry.startedDateTime).getTime();
|
|
133
|
+
// Pega o último request do mesmo host que terminou antes deste começar
|
|
134
|
+
let candidate = null;
|
|
135
|
+
let candidateEnd = 0;
|
|
136
|
+
for (const e of entries) {
|
|
137
|
+
if (e === entry)
|
|
138
|
+
continue;
|
|
139
|
+
if (safeHostname(e.request.url) !== host)
|
|
140
|
+
continue;
|
|
141
|
+
const eEnd = new Date(e.startedDateTime).getTime() + e.time;
|
|
142
|
+
if (eEnd <= start && eEnd > candidateEnd) {
|
|
143
|
+
candidateEnd = eEnd;
|
|
144
|
+
candidate = e;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!candidate)
|
|
148
|
+
return 'connection_pool';
|
|
149
|
+
return `${candidate.request.method} ${truncateUrl(candidate.request.url)}`;
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=networkCapsule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"networkCapsule.js","sourceRoot":"","sources":["../../src/tools/networkCapsule.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAyCvC,MAAM,UAAU,sBAAsB,CAAC,QAAgB;IACrD,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC3C,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAQ,CAAC;IACvC,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC;IAEhC,gBAAgB;IAChB,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9C,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;QAC/B,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM;QACxB,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/B,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM;KAC1B,CAAC,CAAC,CAAC;IAEJ,YAAY;IACZ,MAAM,MAAM,GAAG,OAAO;SACnB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;SAC5D,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;QAC/B,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM;QACzB,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,IAAI,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;KAC5D,CAAC,CAAC,CAAC;IAEN,aAAa;IACb,MAAM,MAAM,GAAwE,EAAE,CAAC;IACvF,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACvE,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC;IAClC,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5C,CAAC,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IAED,sCAAsC;IACtC,MAAM,aAAa,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAEnD,cAAc;IACd,MAAM,QAAQ,GAAG,OAAO;SACrB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC;SAC7C,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;QAC/B,UAAU,EAAE,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC;QACrC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAQ,CAAC;KACxC,CAAC,CAAC,CAAC;IAEN,OAAO;QACL,cAAc,EAAE,OAAO,CAAC,MAAM;QAC9B,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;QACtF,OAAO;QACP,MAAM;QACN,OAAO,EAAE,MAAM;QACf,aAAa;QACb,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,uEAAuE;IACvE,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IAClC,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC;AACnC,CAAC;AAED,SAAS,YAAY,CAAC,GAAW;IAC/B,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,MAAc;IAClC,IAAI,MAAM,IAAI,GAAG;QAAE,OAAO,cAAc,CAAC;IACzC,IAAI,MAAM,IAAI,GAAG;QAAE,OAAO,cAAc,CAAC;IACzC,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,OAAO,CAAC,OAAmB;IAClC,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AACrD,CAAC;AAED;;;;GAIG;AACH,SAAS,mBAAmB,CAAC,OAAmB;IAC9C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAC9B,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,CACxF,CAAC;IAEF,0EAA0E;IAC1E,MAAM,aAAa,GACjB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE;QAC9D,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,IAAI;QAC/B,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,CAAC;IAEjD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5E,MAAM,SAAS,GAAG,aAAa,GAAG,SAAS,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,CAAC;IAE/D,MAAM,MAAM,GAAiB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IACzE,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC;QAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;QACpE,MAAM,CAAC,GAAG,CAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC;IAED,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACzE,IAAI,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,oEAAoE;AACpE,SAAS,aAAa,CAAC,KAAe,EAAE,OAAmB;IACzD,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,CAAC;IACxD,uEAAuE;IACvE,IAAI,SAAS,GAAoB,IAAI,CAAC;IACtC,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,KAAK,KAAK;YAAE,SAAS;QAC1B,IAAI,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI;YAAE,SAAS;QACnD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC;QAC5D,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,GAAG,YAAY,EAAE,CAAC;YACzC,YAAY,GAAG,IAAI,CAAC;YACpB,SAAS,GAAG,CAAC,CAAC;QAChB,CAAC;IACH,CAAC;IACD,IAAI,CAAC,SAAS;QAAE,OAAO,iBAAiB,CAAC;IACzC,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,IAAI,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session_capsule — ÂNCORA do produto. Comprime sessão inteira do agente.
|
|
3
|
+
*
|
|
4
|
+
* FORMATO DIFERENTE: markdown bullets + YAML frontmatter.
|
|
5
|
+
* Razão: economiza ~30% tokens vs JSON pra prosa estruturada.
|
|
6
|
+
* Medido empiricamente — JSON ~95 tok, MD bullets ~67 tok pro mesmo conteúdo.
|
|
7
|
+
*
|
|
8
|
+
* Compressão típica: 50-100k tokens → 1.5-2k (40×).
|
|
9
|
+
*
|
|
10
|
+
* Disparado: automático quando contexto > 60k tokens OU manual via /compress.
|
|
11
|
+
*/
|
|
12
|
+
import type { AgentKind } from '../types.js';
|
|
13
|
+
export interface Turn {
|
|
14
|
+
role: 'user' | 'assistant' | 'tool';
|
|
15
|
+
content: string;
|
|
16
|
+
tool_name?: string;
|
|
17
|
+
tool_input?: unknown;
|
|
18
|
+
timestamp?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function sessionCapsule(turns: Turn[], agent?: AgentKind): Promise<string>;
|
|
21
|
+
//# sourceMappingURL=sessionCapsule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionCapsule.d.ts","sourceRoot":"","sources":["../../src/tools/sessionCapsule.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAA6B,SAAS,EAAE,MAAM,aAAa,CAAC;AAExE,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,cAAc,CAClC,KAAK,EAAE,IAAI,EAAE,EACb,KAAK,GAAE,SAAqB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAuDjB"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session_capsule — ÂNCORA do produto. Comprime sessão inteira do agente.
|
|
3
|
+
*
|
|
4
|
+
* FORMATO DIFERENTE: markdown bullets + YAML frontmatter.
|
|
5
|
+
* Razão: economiza ~30% tokens vs JSON pra prosa estruturada.
|
|
6
|
+
* Medido empiricamente — JSON ~95 tok, MD bullets ~67 tok pro mesmo conteúdo.
|
|
7
|
+
*
|
|
8
|
+
* Compressão típica: 50-100k tokens → 1.5-2k (40×).
|
|
9
|
+
*
|
|
10
|
+
* Disparado: automático quando contexto > 60k tokens OU manual via /compress.
|
|
11
|
+
*/
|
|
12
|
+
import { compressViaLlm } from '../providers/llmClient.js';
|
|
13
|
+
export async function sessionCapsule(turns, agent = 'unknown') {
|
|
14
|
+
if (turns.length === 0) {
|
|
15
|
+
return frontmatter({ agent, turns_compressed: 0 }) + '## Empty session.\n';
|
|
16
|
+
}
|
|
17
|
+
// 1. Dedupe turns repetitivos (3× Read mesmo file → 1 entry)
|
|
18
|
+
const dedup = deduplicateTurns(turns);
|
|
19
|
+
// 2. Chunk pra caber em context do Haiku (40k tokens max)
|
|
20
|
+
const chunks = chunkByApproxTokens(dedup, 40_000);
|
|
21
|
+
let summary;
|
|
22
|
+
let provider = 'unknown';
|
|
23
|
+
if (chunks.length === 1) {
|
|
24
|
+
// 1 chunk → 1 chamada LLM direta
|
|
25
|
+
const llm = await compressViaLlm({
|
|
26
|
+
capsule_type: 'session',
|
|
27
|
+
prompt: SESSION_PROMPT_DIRECT,
|
|
28
|
+
input: { turns: chunks[0], agent },
|
|
29
|
+
model_preference: 'cheap'
|
|
30
|
+
});
|
|
31
|
+
summary = llm?.result ?? fallbackSummary(dedup);
|
|
32
|
+
provider = llm?.provider_used ?? 'fallback';
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// N chunks → map-reduce
|
|
36
|
+
const partials = [];
|
|
37
|
+
for (const chunk of chunks) {
|
|
38
|
+
const r = await compressViaLlm({
|
|
39
|
+
capsule_type: 'session',
|
|
40
|
+
prompt: SESSION_PROMPT_PARTIAL,
|
|
41
|
+
input: { turns: chunk, agent },
|
|
42
|
+
model_preference: 'cheap'
|
|
43
|
+
});
|
|
44
|
+
partials.push(r?.result ?? '');
|
|
45
|
+
}
|
|
46
|
+
const reduceR = await compressViaLlm({
|
|
47
|
+
capsule_type: 'session',
|
|
48
|
+
prompt: SESSION_PROMPT_REDUCE,
|
|
49
|
+
input: { partials, agent },
|
|
50
|
+
model_preference: 'balanced'
|
|
51
|
+
});
|
|
52
|
+
summary = reduceR?.result ?? partials.join('\n\n');
|
|
53
|
+
provider = reduceR?.provider_used ?? 'fallback';
|
|
54
|
+
}
|
|
55
|
+
const fm = {
|
|
56
|
+
timestamp: new Date().toISOString(),
|
|
57
|
+
tokens_saved: 0, // setado pelo intercept depois de medir
|
|
58
|
+
provider_used: provider,
|
|
59
|
+
agent,
|
|
60
|
+
turns_compressed: turns.length
|
|
61
|
+
};
|
|
62
|
+
return frontmatter(fm) + summary.trim() + '\n';
|
|
63
|
+
}
|
|
64
|
+
function frontmatter(fm) {
|
|
65
|
+
const lines = ['---'];
|
|
66
|
+
for (const [k, v] of Object.entries(fm)) {
|
|
67
|
+
lines.push(`${k}: ${typeof v === 'string' ? JSON.stringify(v) : v}`);
|
|
68
|
+
}
|
|
69
|
+
lines.push('---', '');
|
|
70
|
+
return lines.join('\n');
|
|
71
|
+
}
|
|
72
|
+
function deduplicateTurns(turns) {
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
const result = [];
|
|
75
|
+
for (const t of turns) {
|
|
76
|
+
// Hash: role + tool_name + first 200 chars content
|
|
77
|
+
const sig = `${t.role}|${t.tool_name ?? ''}|${(t.content ?? '').slice(0, 200)}`;
|
|
78
|
+
if (seen.has(sig))
|
|
79
|
+
continue;
|
|
80
|
+
seen.add(sig);
|
|
81
|
+
result.push(t);
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
function chunkByApproxTokens(turns, maxTokens) {
|
|
86
|
+
const chunks = [[]];
|
|
87
|
+
let current = 0;
|
|
88
|
+
for (const t of turns) {
|
|
89
|
+
const approxTokens = Math.ceil((t.content?.length ?? 0) / 3.5);
|
|
90
|
+
if (current + approxTokens > maxTokens && chunks[chunks.length - 1].length > 0) {
|
|
91
|
+
chunks.push([]);
|
|
92
|
+
current = 0;
|
|
93
|
+
}
|
|
94
|
+
chunks[chunks.length - 1].push(t);
|
|
95
|
+
current += approxTokens;
|
|
96
|
+
}
|
|
97
|
+
return chunks;
|
|
98
|
+
}
|
|
99
|
+
function fallbackSummary(turns) {
|
|
100
|
+
const userInputs = turns.filter((t) => t.role === 'user').slice(0, 3);
|
|
101
|
+
const tools = new Set(turns.map((t) => t.tool_name).filter(Boolean));
|
|
102
|
+
return [
|
|
103
|
+
'## Investigated',
|
|
104
|
+
...userInputs.map((t) => `- ${t.content.slice(0, 200)}`),
|
|
105
|
+
'',
|
|
106
|
+
'## Tools used',
|
|
107
|
+
...Array.from(tools).map((t) => `- ${t}`),
|
|
108
|
+
'',
|
|
109
|
+
'## Notes',
|
|
110
|
+
'- Fallback summary (LLM unavailable). Full context preserved in original session.'
|
|
111
|
+
].join('\n');
|
|
112
|
+
}
|
|
113
|
+
const SESSION_PROMPT_DIRECT = `
|
|
114
|
+
Você recebe os turns de uma sessão de trabalho de agente IA.
|
|
115
|
+
Devolva APENAS markdown bullets (NÃO JSON):
|
|
116
|
+
|
|
117
|
+
## Investigated
|
|
118
|
+
- bullets do que o agente explorou (problemas, áreas)
|
|
119
|
+
|
|
120
|
+
## Findings
|
|
121
|
+
- bullets com descobertas concretas (citar evidência)
|
|
122
|
+
|
|
123
|
+
## Decisions
|
|
124
|
+
- bullets com decisões tomadas (com razão)
|
|
125
|
+
|
|
126
|
+
## Pending
|
|
127
|
+
- bullets com tarefas/perguntas em aberto
|
|
128
|
+
|
|
129
|
+
## Relevant files
|
|
130
|
+
- "caminho/arquivo.ts:linha" — por quê é relevante
|
|
131
|
+
|
|
132
|
+
Foque no que importa pro PRÓXIMO agente continuar. Descarte exploração que não levou a nada.
|
|
133
|
+
Sem prosa de introdução, sem markdown além dos headers ## e bullets.
|
|
134
|
+
`;
|
|
135
|
+
const SESSION_PROMPT_PARTIAL = `
|
|
136
|
+
Você recebe um pedaço (chunk) de uma sessão grande.
|
|
137
|
+
Resuma APENAS este pedaço em markdown bullets:
|
|
138
|
+
|
|
139
|
+
## Investigated (neste chunk)
|
|
140
|
+
- bullets
|
|
141
|
+
|
|
142
|
+
## Findings (neste chunk)
|
|
143
|
+
- bullets
|
|
144
|
+
|
|
145
|
+
## Decisions (neste chunk)
|
|
146
|
+
- bullets
|
|
147
|
+
|
|
148
|
+
## Pending (neste chunk)
|
|
149
|
+
- bullets
|
|
150
|
+
|
|
151
|
+
Sem prosa extra. Vai virar input pra reduce final.
|
|
152
|
+
`;
|
|
153
|
+
const SESSION_PROMPT_REDUCE = `
|
|
154
|
+
Você recebe N resumos parciais de chunks de uma sessão.
|
|
155
|
+
Consolide num único resumo final em markdown bullets:
|
|
156
|
+
|
|
157
|
+
## Investigated
|
|
158
|
+
- bullets consolidados (dedupe, prioridade ao mais recente)
|
|
159
|
+
|
|
160
|
+
## Findings
|
|
161
|
+
- bullets consolidados
|
|
162
|
+
|
|
163
|
+
## Decisions
|
|
164
|
+
- bullets consolidados (decisão mais recente vence)
|
|
165
|
+
|
|
166
|
+
## Pending
|
|
167
|
+
- bullets do que ainda falta
|
|
168
|
+
|
|
169
|
+
## Relevant files
|
|
170
|
+
- "path:line" — por quê
|
|
171
|
+
|
|
172
|
+
Sem prosa de introdução.
|
|
173
|
+
`;
|
|
174
|
+
//# sourceMappingURL=sessionCapsule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionCapsule.js","sourceRoot":"","sources":["../../src/tools/sessionCapsule.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sql_capsule — comprime slow query log (Postgres/MySQL).
|
|
3
|
+
* Parse + normaliza shape + agrupa + LLM sugere índice.
|
|
4
|
+
*
|
|
5
|
+
* Compressão típica: 50k tokens → 2k (25×).
|
|
6
|
+
*/
|
|
7
|
+
import type { SqlCapsule } from '../types.js';
|
|
8
|
+
export interface ParsedSlowQuery {
|
|
9
|
+
query: string;
|
|
10
|
+
duration_ms: number;
|
|
11
|
+
rows_affected?: number;
|
|
12
|
+
database?: string;
|
|
13
|
+
user?: string;
|
|
14
|
+
timestamp?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function sqlCapsule(rawLog: string, source?: SqlCapsule['source']): Promise<SqlCapsule>;
|
|
17
|
+
//# sourceMappingURL=sqlCapsule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlCapsule.d.ts","sourceRoot":"","sources":["../../src/tools/sqlCapsule.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,UAAU,CAAC,QAAQ,CAAc,GACxC,OAAO,CAAC,UAAU,CAAC,CA4DrB"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sql_capsule — comprime slow query log (Postgres/MySQL).
|
|
3
|
+
* Parse + normaliza shape + agrupa + LLM sugere índice.
|
|
4
|
+
*
|
|
5
|
+
* Compressão típica: 50k tokens → 2k (25×).
|
|
6
|
+
*/
|
|
7
|
+
import { compressViaLlm } from '../providers/llmClient.js';
|
|
8
|
+
export async function sqlCapsule(rawLog, source = 'postgres') {
|
|
9
|
+
const queries = parseSlowLog(rawLog, source);
|
|
10
|
+
if (queries.length === 0) {
|
|
11
|
+
return { patterns: [], total_queries: 0, source };
|
|
12
|
+
}
|
|
13
|
+
// 1. Normaliza shape (algoritmo) — agrupa queries iguais com params diferentes
|
|
14
|
+
const groups = groupByShape(queries);
|
|
15
|
+
// 2. Ranking por impact (count × avg_ms)
|
|
16
|
+
const ranked = Array.from(groups.entries())
|
|
17
|
+
.map(([shape, items]) => {
|
|
18
|
+
const total_ms = items.reduce((acc, q) => acc + q.duration_ms, 0);
|
|
19
|
+
const sorted = [...items].sort((a, b) => a.duration_ms - b.duration_ms);
|
|
20
|
+
const p95 = sorted[Math.floor(sorted.length * 0.95)]?.duration_ms ?? items[0].duration_ms;
|
|
21
|
+
return {
|
|
22
|
+
shape,
|
|
23
|
+
count: items.length,
|
|
24
|
+
total_ms,
|
|
25
|
+
avg_ms: Math.round(total_ms / items.length),
|
|
26
|
+
p95_ms: p95,
|
|
27
|
+
sample_query: items[0].query
|
|
28
|
+
};
|
|
29
|
+
})
|
|
30
|
+
.sort((a, b) => b.total_ms - a.total_ms)
|
|
31
|
+
.slice(0, 10);
|
|
32
|
+
// 3. LLM sugere índice pra top patterns (custo: 10× $0.0001 = $0.001)
|
|
33
|
+
const withSuggestions = await Promise.all(ranked.map(async (p) => {
|
|
34
|
+
const llm = await compressViaLlm({
|
|
35
|
+
capsule_type: 'sql',
|
|
36
|
+
prompt: SQL_PROMPT,
|
|
37
|
+
input: {
|
|
38
|
+
query: p.sample_query,
|
|
39
|
+
avg_ms: p.avg_ms,
|
|
40
|
+
count: p.count,
|
|
41
|
+
source
|
|
42
|
+
},
|
|
43
|
+
model_preference: 'cheap'
|
|
44
|
+
});
|
|
45
|
+
if (!llm) {
|
|
46
|
+
return p; // sem suggested_index se LLM falhar
|
|
47
|
+
}
|
|
48
|
+
const out = llm.result;
|
|
49
|
+
return {
|
|
50
|
+
...p,
|
|
51
|
+
suggested_index: out.suggested_index,
|
|
52
|
+
explanation: out.explanation
|
|
53
|
+
};
|
|
54
|
+
}));
|
|
55
|
+
return {
|
|
56
|
+
patterns: withSuggestions,
|
|
57
|
+
total_queries: queries.length,
|
|
58
|
+
source
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Normaliza query: substitui literais por $N. */
|
|
62
|
+
function normalizeShape(query) {
|
|
63
|
+
return query
|
|
64
|
+
// Strings entre aspas
|
|
65
|
+
.replace(/'[^']*'/g, "'$STR'")
|
|
66
|
+
// Numeros
|
|
67
|
+
.replace(/\b\d+(\.\d+)?\b/g, '$NUM')
|
|
68
|
+
// Listas IN (1,2,3) — qualquer tamanho
|
|
69
|
+
.replace(/IN\s*\([^)]+\)/gi, 'IN ($LIST)')
|
|
70
|
+
// Espaços normalizados
|
|
71
|
+
.replace(/\s+/g, ' ')
|
|
72
|
+
.trim();
|
|
73
|
+
}
|
|
74
|
+
function groupByShape(queries) {
|
|
75
|
+
const groups = new Map();
|
|
76
|
+
for (const q of queries) {
|
|
77
|
+
const shape = normalizeShape(q.query);
|
|
78
|
+
if (!groups.has(shape))
|
|
79
|
+
groups.set(shape, []);
|
|
80
|
+
groups.get(shape).push(q);
|
|
81
|
+
}
|
|
82
|
+
return groups;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Parser de slow log. Detecta formato por source.
|
|
86
|
+
* Postgres: linhas com "duration: NNN.NNN ms" + query depois
|
|
87
|
+
* MySQL: blocos com "# Query_time: N" + USE + query
|
|
88
|
+
*/
|
|
89
|
+
function parseSlowLog(raw, source) {
|
|
90
|
+
if (source === 'postgres')
|
|
91
|
+
return parsePostgres(raw);
|
|
92
|
+
if (source === 'mysql')
|
|
93
|
+
return parseMysql(raw);
|
|
94
|
+
// Fallback: tenta postgres primeiro, depois mysql
|
|
95
|
+
const pg = parsePostgres(raw);
|
|
96
|
+
if (pg.length > 0)
|
|
97
|
+
return pg;
|
|
98
|
+
return parseMysql(raw);
|
|
99
|
+
}
|
|
100
|
+
function parsePostgres(raw) {
|
|
101
|
+
const queries = [];
|
|
102
|
+
// Pattern: "LOG: duration: 1234.567 ms statement: SELECT ..."
|
|
103
|
+
// OU: "LOG: duration: 1234.567 ms execute <unnamed>: SELECT ..."
|
|
104
|
+
const pattern = /duration:\s*(\d+(?:\.\d+)?)\s*ms\s+(?:statement|execute(?:\s+<\w+>)?):\s*(.+?)(?=\n\d{4}-|\nLOG:|\n*$)/gs;
|
|
105
|
+
let m;
|
|
106
|
+
while ((m = pattern.exec(raw)) !== null) {
|
|
107
|
+
queries.push({
|
|
108
|
+
query: m[2].trim().replace(/\s+/g, ' '),
|
|
109
|
+
duration_ms: parseFloat(m[1])
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return queries;
|
|
113
|
+
}
|
|
114
|
+
function parseMysql(raw) {
|
|
115
|
+
const queries = [];
|
|
116
|
+
// Blocos separados por "# Time: ..."
|
|
117
|
+
const blocks = raw.split(/^# Time:/m).slice(1);
|
|
118
|
+
for (const block of blocks) {
|
|
119
|
+
const queryTimeMatch = block.match(/# Query_time:\s*(\d+(?:\.\d+)?)/);
|
|
120
|
+
if (!queryTimeMatch)
|
|
121
|
+
continue;
|
|
122
|
+
const duration_ms = parseFloat(queryTimeMatch[1]) * 1000;
|
|
123
|
+
// Query é tudo depois do último "# ..." até o próximo bloco
|
|
124
|
+
const queryMatch = block.match(/(?:^|\n)([^#\n].+?;)/s);
|
|
125
|
+
if (!queryMatch)
|
|
126
|
+
continue;
|
|
127
|
+
queries.push({
|
|
128
|
+
query: queryMatch[1].trim().replace(/\s+/g, ' '),
|
|
129
|
+
duration_ms
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return queries;
|
|
133
|
+
}
|
|
134
|
+
const SQL_PROMPT = `
|
|
135
|
+
Você recebe uma query SQL lenta com estatísticas.
|
|
136
|
+
Sugira um índice que reduziria o tempo OU explique se a query é mal escrita.
|
|
137
|
+
Devolva APENAS JSON:
|
|
138
|
+
{
|
|
139
|
+
"suggested_index": "CREATE INDEX ... ON ... (...) — ou null se não aplicável",
|
|
140
|
+
"explanation": "1 frase curta explicando por quê esse índice ajuda OU o que está errado na query"
|
|
141
|
+
}
|
|
142
|
+
Sem markdown, JSON puro.
|
|
143
|
+
`;
|
|
144
|
+
//# sourceMappingURL=sqlCapsule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlCapsule.js","sourceRoot":"","sources":["../../src/tools/sqlCapsule.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stack_capsule — comprime stack trace de erro client-side.
|
|
3
|
+
* Regex parser + LLM mini (Haiku/Flash/DeepSeek via router).
|
|
4
|
+
*
|
|
5
|
+
* Compressão típica: 8k tokens → 500 (16×).
|
|
6
|
+
*/
|
|
7
|
+
import type { StackCapsule } from '../types.js';
|
|
8
|
+
export declare function stackCapsule(trace: string, source?: 'sentry' | 'console' | 'node'): Promise<StackCapsule>;
|
|
9
|
+
//# sourceMappingURL=stackCapsule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stackCapsule.d.ts","sourceRoot":"","sources":["../../src/tools/stackCapsule.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAmChD,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,GACrC,OAAO,CAAC,YAAY,CAAC,CA4CvB"}
|