context-lens 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -13
- package/dist/cli-utils.d.ts +1 -0
- package/dist/cli-utils.d.ts.map +1 -1
- package/dist/cli-utils.js +12 -0
- package/dist/cli-utils.js.map +1 -1
- package/dist/cli.js +110 -6
- package/dist/cli.js.map +1 -1
- package/dist/core/conversation.d.ts +10 -1
- package/dist/core/conversation.d.ts.map +1 -1
- package/dist/core/conversation.js +118 -1
- package/dist/core/conversation.js.map +1 -1
- package/dist/core/health.d.ts +7 -0
- package/dist/core/health.d.ts.map +1 -0
- package/dist/core/health.js +311 -0
- package/dist/core/health.js.map +1 -0
- package/dist/core/models.d.ts +8 -2
- package/dist/core/models.d.ts.map +1 -1
- package/dist/core/models.js +18 -3
- package/dist/core/models.js.map +1 -1
- package/dist/core/security.d.ts +8 -0
- package/dist/core/security.d.ts.map +1 -0
- package/dist/core/security.js +222 -0
- package/dist/core/security.js.map +1 -0
- package/dist/core/tokens.d.ts +20 -2
- package/dist/core/tokens.d.ts.map +1 -1
- package/dist/core/tokens.js +151 -4
- package/dist/core/tokens.js.map +1 -1
- package/dist/core.d.ts +4 -2
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +4 -2
- package/dist/core.js.map +1 -1
- package/dist/lhar/composition.d.ts +12 -0
- package/dist/lhar/composition.d.ts.map +1 -0
- package/dist/lhar/composition.js +258 -0
- package/dist/lhar/composition.js.map +1 -0
- package/dist/lhar/export.d.ts +5 -0
- package/dist/lhar/export.d.ts.map +1 -0
- package/dist/lhar/export.js +59 -0
- package/dist/lhar/export.js.map +1 -0
- package/dist/lhar/record.d.ts +6 -0
- package/dist/lhar/record.d.ts.map +1 -0
- package/dist/lhar/record.js +216 -0
- package/dist/lhar/record.js.map +1 -0
- package/dist/lhar/response.d.ts +11 -0
- package/dist/lhar/response.d.ts.map +1 -0
- package/dist/lhar/response.js +132 -0
- package/dist/lhar/response.js.map +1 -0
- package/dist/lhar-types.generated.d.ts +23 -2
- package/dist/lhar-types.generated.d.ts.map +1 -1
- package/dist/lhar.d.ts +12 -19
- package/dist/lhar.d.ts.map +1 -1
- package/dist/lhar.js +16 -557
- package/dist/lhar.js.map +1 -1
- package/dist/server/api.d.ts +8 -0
- package/dist/server/api.d.ts.map +1 -0
- package/dist/server/api.js +292 -0
- package/dist/server/api.js.map +1 -0
- package/dist/server/config.d.ts +2 -1
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/config.js +4 -1
- package/dist/server/config.js.map +1 -1
- package/dist/server/projection.d.ts.map +1 -1
- package/dist/server/projection.js +17 -9
- package/dist/server/projection.js.map +1 -1
- package/dist/server/proxy.d.ts.map +1 -1
- package/dist/server/proxy.js +36 -50
- package/dist/server/proxy.js.map +1 -1
- package/dist/server/static.d.ts +9 -0
- package/dist/server/static.d.ts.map +1 -0
- package/dist/server/static.js +78 -0
- package/dist/server/static.js.map +1 -0
- package/dist/server/store.d.ts +49 -1
- package/dist/server/store.d.ts.map +1 -1
- package/dist/server/store.js +297 -15
- package/dist/server/store.js.map +1 -1
- package/dist/server/webui.d.ts +2 -2
- package/dist/server/webui.d.ts.map +1 -1
- package/dist/server/webui.js +32 -160
- package/dist/server/webui.js.map +1 -1
- package/dist/server.js +3 -2
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +44 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/findings-screenshot.png +0 -0
- package/messages-screenshot.png +0 -0
- package/package.json +9 -8
- package/schema/lhar.schema.json +57 -3
- package/screenshot-overview.png +0 -0
- package/sessions-screenshot.png +0 -0
- package/timeline-screenshot.png +0 -0
- package/diff.png +0 -0
- package/overview-sidebar.png +0 -0
- package/public/index.html +0 -2831
package/dist/core/tokens.d.ts
CHANGED
|
@@ -1,11 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Lightweight token estimator used throughout Context Lens.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Approximates tokens as `ceil(chars / 4)`. For image content blocks,
|
|
5
|
+
* uses a fixed per-image estimate instead of stringifying base64 data.
|
|
6
6
|
*
|
|
7
7
|
* @param text - Value to estimate tokens for. Objects are stringified as JSON.
|
|
8
8
|
* @returns Estimated token count (>= 0).
|
|
9
9
|
*/
|
|
10
10
|
export declare function estimateTokens(text: unknown): number;
|
|
11
|
+
/**
|
|
12
|
+
* Rescale all token sub-fields in a ContextInfo so they are internally
|
|
13
|
+
* consistent with an authoritative total (typically from API usage data).
|
|
14
|
+
*
|
|
15
|
+
* Adjusts `systemTokens`, `toolsTokens`, per-message `.tokens`, and
|
|
16
|
+
* `messagesTokens` proportionally, then applies a rounding residual fix
|
|
17
|
+
* to `messagesTokens` so the invariant
|
|
18
|
+
* `totalTokens === systemTokens + toolsTokens + messagesTokens` holds exactly.
|
|
19
|
+
*/
|
|
20
|
+
export declare function rescaleContextTokens(ci: {
|
|
21
|
+
systemTokens: number;
|
|
22
|
+
toolsTokens: number;
|
|
23
|
+
messagesTokens: number;
|
|
24
|
+
totalTokens: number;
|
|
25
|
+
messages: {
|
|
26
|
+
tokens: number;
|
|
27
|
+
}[];
|
|
28
|
+
}, authoritative: number): void;
|
|
11
29
|
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/core/tokens.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/core/tokens.ts"],"names":[],"mappings":"AA8DA;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAqBpD;AAiCD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,EAAE,EAAE;IACF,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAChC,EACD,aAAa,EAAE,MAAM,GACpB,IAAI,CAyCN"}
|
package/dist/core/tokens.js
CHANGED
|
@@ -1,8 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approximate token cost for a single image.
|
|
3
|
+
*
|
|
4
|
+
* Anthropic charges based on image dimensions (~1,600 tokens per 512x512 tile).
|
|
5
|
+
* Since we don't decode image data, we use a conservative flat estimate of 1,600
|
|
6
|
+
* tokens (one tile). Most screenshots cost 2,000-6,400 tokens, so this slightly
|
|
7
|
+
* under-counts but is far better than stringifying megabytes of base64.
|
|
8
|
+
*/
|
|
9
|
+
const IMAGE_TOKEN_ESTIMATE = 1_600;
|
|
10
|
+
/**
|
|
11
|
+
* Return true if the value looks like an image content block.
|
|
12
|
+
*
|
|
13
|
+
* Matches Anthropic `{type:"image", source:{type:"base64", data:"..."}}`,
|
|
14
|
+
* OpenAI `{type:"image_url", image_url:{url:"data:..."}}`,
|
|
15
|
+
* and Gemini `{inlineData:{...}}` / `{fileData:{...}}`.
|
|
16
|
+
*/
|
|
17
|
+
function isImageBlock(val) {
|
|
18
|
+
if (!val || typeof val !== "object" || Array.isArray(val))
|
|
19
|
+
return false;
|
|
20
|
+
const obj = val;
|
|
21
|
+
if (obj.type === "image" || obj.type === "image_url")
|
|
22
|
+
return true;
|
|
23
|
+
if (obj.inlineData || obj.fileData)
|
|
24
|
+
return true;
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Strip base64 image data from a value before stringifying for token estimation.
|
|
29
|
+
*
|
|
30
|
+
* Walks arrays and recognizes image blocks at any nesting level (top-level content
|
|
31
|
+
* arrays, tool_result content arrays, Gemini parts). Image blocks are replaced with
|
|
32
|
+
* a sentinel so the rest of the structure is still counted.
|
|
33
|
+
*/
|
|
34
|
+
function stripImageData(val) {
|
|
35
|
+
if (!val || typeof val !== "object")
|
|
36
|
+
return val;
|
|
37
|
+
if (Array.isArray(val)) {
|
|
38
|
+
return val.map(stripImageData);
|
|
39
|
+
}
|
|
40
|
+
// Image block: return a lightweight placeholder
|
|
41
|
+
if (isImageBlock(val)) {
|
|
42
|
+
return {
|
|
43
|
+
type: val.type || "image",
|
|
44
|
+
_image: true,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const obj = val;
|
|
48
|
+
// tool_result blocks can nest image blocks inside their content array
|
|
49
|
+
if (obj.type === "tool_result" && Array.isArray(obj.content)) {
|
|
50
|
+
return { ...obj, content: obj.content.map(stripImageData) };
|
|
51
|
+
}
|
|
52
|
+
// Gemini turn: parts array may contain inlineData
|
|
53
|
+
if (Array.isArray(obj.parts)) {
|
|
54
|
+
return { ...obj, parts: obj.parts.map(stripImageData) };
|
|
55
|
+
}
|
|
56
|
+
return obj;
|
|
57
|
+
}
|
|
1
58
|
/**
|
|
2
59
|
* Lightweight token estimator used throughout Context Lens.
|
|
3
60
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
61
|
+
* Approximates tokens as `ceil(chars / 4)`. For image content blocks,
|
|
62
|
+
* uses a fixed per-image estimate instead of stringifying base64 data.
|
|
6
63
|
*
|
|
7
64
|
* @param text - Value to estimate tokens for. Objects are stringified as JSON.
|
|
8
65
|
* @returns Estimated token count (>= 0).
|
|
@@ -10,7 +67,97 @@
|
|
|
10
67
|
export function estimateTokens(text) {
|
|
11
68
|
if (!text)
|
|
12
69
|
return 0;
|
|
13
|
-
|
|
14
|
-
|
|
70
|
+
// Fast path: plain strings never contain image data
|
|
71
|
+
if (typeof text === "string") {
|
|
72
|
+
return Math.ceil(text.length / 4);
|
|
73
|
+
}
|
|
74
|
+
// Single image block
|
|
75
|
+
if (isImageBlock(text)) {
|
|
76
|
+
return IMAGE_TOKEN_ESTIMATE;
|
|
77
|
+
}
|
|
78
|
+
// Object/array: strip image data, then stringify the rest
|
|
79
|
+
const cleaned = stripImageData(text);
|
|
80
|
+
const s = JSON.stringify(cleaned);
|
|
81
|
+
const baseTokens = Math.ceil(s.length / 4);
|
|
82
|
+
// Count image blocks and add fixed estimate for each
|
|
83
|
+
const imageCount = countImages(text);
|
|
84
|
+
return baseTokens + imageCount * IMAGE_TOKEN_ESTIMATE;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Count the number of image blocks in a value (recursive).
|
|
88
|
+
*/
|
|
89
|
+
function countImages(val) {
|
|
90
|
+
if (!val || typeof val !== "object")
|
|
91
|
+
return 0;
|
|
92
|
+
if (isImageBlock(val))
|
|
93
|
+
return 1;
|
|
94
|
+
if (Array.isArray(val)) {
|
|
95
|
+
let count = 0;
|
|
96
|
+
for (const item of val) {
|
|
97
|
+
count += countImages(item);
|
|
98
|
+
}
|
|
99
|
+
return count;
|
|
100
|
+
}
|
|
101
|
+
const obj = val;
|
|
102
|
+
// Check nested content in tool_result blocks
|
|
103
|
+
if (obj.type === "tool_result" && Array.isArray(obj.content)) {
|
|
104
|
+
return countImages(obj.content);
|
|
105
|
+
}
|
|
106
|
+
// Check Gemini parts
|
|
107
|
+
if (Array.isArray(obj.parts)) {
|
|
108
|
+
return countImages(obj.parts);
|
|
109
|
+
}
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Rescale all token sub-fields in a ContextInfo so they are internally
|
|
114
|
+
* consistent with an authoritative total (typically from API usage data).
|
|
115
|
+
*
|
|
116
|
+
* Adjusts `systemTokens`, `toolsTokens`, per-message `.tokens`, and
|
|
117
|
+
* `messagesTokens` proportionally, then applies a rounding residual fix
|
|
118
|
+
* to `messagesTokens` so the invariant
|
|
119
|
+
* `totalTokens === systemTokens + toolsTokens + messagesTokens` holds exactly.
|
|
120
|
+
*/
|
|
121
|
+
export function rescaleContextTokens(ci, authoritative) {
|
|
122
|
+
const estimated = ci.systemTokens + ci.toolsTokens + ci.messagesTokens;
|
|
123
|
+
if (estimated === 0 || authoritative === 0) {
|
|
124
|
+
ci.systemTokens = 0;
|
|
125
|
+
ci.toolsTokens = 0;
|
|
126
|
+
ci.messagesTokens = 0;
|
|
127
|
+
ci.totalTokens = authoritative;
|
|
128
|
+
for (const msg of ci.messages) {
|
|
129
|
+
msg.tokens = 0;
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (authoritative === estimated) {
|
|
134
|
+
ci.totalTokens = authoritative;
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const scale = authoritative / estimated;
|
|
138
|
+
ci.systemTokens = Math.round(ci.systemTokens * scale);
|
|
139
|
+
ci.toolsTokens = Math.round(ci.toolsTokens * scale);
|
|
140
|
+
for (const msg of ci.messages) {
|
|
141
|
+
msg.tokens = Math.round(msg.tokens * scale);
|
|
142
|
+
}
|
|
143
|
+
ci.messagesTokens = ci.messages.reduce((s, m) => s + m.tokens, 0);
|
|
144
|
+
ci.totalTokens = authoritative;
|
|
145
|
+
// Fix rounding residual so both invariants hold:
|
|
146
|
+
// totalTokens === systemTokens + toolsTokens + messagesTokens
|
|
147
|
+
// messagesTokens === sum(msg.tokens)
|
|
148
|
+
const residual = authoritative - (ci.systemTokens + ci.toolsTokens + ci.messagesTokens);
|
|
149
|
+
if (residual !== 0) {
|
|
150
|
+
ci.messagesTokens += residual;
|
|
151
|
+
// Push the residual into the largest per-message entry (or first if tied)
|
|
152
|
+
// so sum(msg.tokens) stays equal to messagesTokens.
|
|
153
|
+
if (ci.messages.length > 0) {
|
|
154
|
+
let maxIdx = 0;
|
|
155
|
+
for (let i = 1; i < ci.messages.length; i++) {
|
|
156
|
+
if (ci.messages[i].tokens > ci.messages[maxIdx].tokens)
|
|
157
|
+
maxIdx = i;
|
|
158
|
+
}
|
|
159
|
+
ci.messages[maxIdx].tokens += residual;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
15
162
|
}
|
|
16
163
|
//# sourceMappingURL=tokens.js.map
|
package/dist/core/tokens.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/core/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,IAAa;IAC1C,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/core/tokens.ts"],"names":[],"mappings":"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"}
|
package/dist/core.d.ts
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* This is intentionally a thin facade: other parts of the codebase import from here,
|
|
5
5
|
* while implementations live in `src/core/*` to keep concerns separated.
|
|
6
6
|
*/
|
|
7
|
-
export { computeAgentKey, computeFingerprint, extractConversationLabel, extractReadableText, extractSessionId, extractUserPrompt, extractWorkingDirectory, } from "./core/conversation.js";
|
|
7
|
+
export { computeAgentKey, computeFingerprint, extractConversationLabel, extractReadableText, extractSessionId, extractToolsUsed, extractUserPrompt, extractWorkingDirectory, } from "./core/conversation.js";
|
|
8
|
+
export { computeHealthScore } from "./core/health.js";
|
|
8
9
|
export { estimateCost, getContextLimit, } from "./core/models.js";
|
|
9
10
|
export { parseContextInfo } from "./core/parse.js";
|
|
10
11
|
export { detectApiFormat, detectProvider, extractSource, resolveTargetUrl, } from "./core/routing.js";
|
|
12
|
+
export { scanSecurity } from "./core/security.js";
|
|
11
13
|
export { detectSource } from "./core/source.js";
|
|
12
|
-
export { estimateTokens } from "./core/tokens.js";
|
|
14
|
+
export { estimateTokens, rescaleContextTokens } from "./core/tokens.js";
|
|
13
15
|
//# sourceMappingURL=core.d.ts.map
|
package/dist/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,wBAAwB,EACxB,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,YAAY,EACZ,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,eAAe,EACf,cAAc,EACd,aAAa,EACb,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,wBAAwB,EACxB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EACL,YAAY,EACZ,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,eAAe,EACf,cAAc,EACd,aAAa,EACb,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/core.js
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* This is intentionally a thin facade: other parts of the codebase import from here,
|
|
5
5
|
* while implementations live in `src/core/*` to keep concerns separated.
|
|
6
6
|
*/
|
|
7
|
-
export { computeAgentKey, computeFingerprint, extractConversationLabel, extractReadableText, extractSessionId, extractUserPrompt, extractWorkingDirectory, } from "./core/conversation.js";
|
|
7
|
+
export { computeAgentKey, computeFingerprint, extractConversationLabel, extractReadableText, extractSessionId, extractToolsUsed, extractUserPrompt, extractWorkingDirectory, } from "./core/conversation.js";
|
|
8
|
+
export { computeHealthScore } from "./core/health.js";
|
|
8
9
|
export { estimateCost, getContextLimit, } from "./core/models.js";
|
|
9
10
|
export { parseContextInfo } from "./core/parse.js";
|
|
10
11
|
export { detectApiFormat, detectProvider, extractSource, resolveTargetUrl, } from "./core/routing.js";
|
|
12
|
+
export { scanSecurity } from "./core/security.js";
|
|
11
13
|
export { detectSource } from "./core/source.js";
|
|
12
|
-
export { estimateTokens } from "./core/tokens.js";
|
|
14
|
+
export { estimateTokens, rescaleContextTokens } from "./core/tokens.js";
|
|
13
15
|
//# sourceMappingURL=core.js.map
|
package/dist/core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,wBAAwB,EACxB,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,YAAY,EACZ,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,eAAe,EACf,cAAc,EACd,aAAa,EACb,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,wBAAwB,EACxB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EACL,YAAY,EACZ,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,eAAe,EACf,cAAc,EACd,aAAa,EACb,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CompositionEntry } from "../lhar-types.generated.js";
|
|
2
|
+
import type { ContextInfo } from "../types.js";
|
|
3
|
+
export declare function analyzeComposition(contextInfo: ContextInfo, rawBody: Record<string, any> | undefined): CompositionEntry[];
|
|
4
|
+
/**
|
|
5
|
+
* Normalize composition token counts so their sum equals an authoritative total.
|
|
6
|
+
*
|
|
7
|
+
* Scales each entry proportionally and applies a rounding residual fix on the
|
|
8
|
+
* largest entry so `sum(composition[].tokens) === authoritative` exactly.
|
|
9
|
+
* Also recomputes `pct` fields.
|
|
10
|
+
*/
|
|
11
|
+
export declare function normalizeComposition(composition: CompositionEntry[], authoritative: number): void;
|
|
12
|
+
//# sourceMappingURL=composition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../../src/lhar/composition.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,gBAAgB,EACjB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GACvC,gBAAgB,EAAE,CAwEpB;AA8KD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,gBAAgB,EAAE,EAC/B,aAAa,EAAE,MAAM,GACpB,IAAI,CAgCN"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { estimateTokens } from "../core.js";
|
|
2
|
+
export function analyzeComposition(contextInfo, rawBody) {
|
|
3
|
+
const counts = new Map();
|
|
4
|
+
function add(category, tokens) {
|
|
5
|
+
const existing = counts.get(category);
|
|
6
|
+
if (existing) {
|
|
7
|
+
existing.tokens += tokens;
|
|
8
|
+
existing.count += 1;
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
counts.set(category, { tokens, count: 1 });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
if (!rawBody) {
|
|
15
|
+
// Fallback to contextInfo aggregates
|
|
16
|
+
if (contextInfo.systemTokens > 0)
|
|
17
|
+
add("system_prompt", contextInfo.systemTokens);
|
|
18
|
+
if (contextInfo.toolsTokens > 0)
|
|
19
|
+
add("tool_definitions", contextInfo.toolsTokens);
|
|
20
|
+
if (contextInfo.messagesTokens > 0)
|
|
21
|
+
add("other", contextInfo.messagesTokens);
|
|
22
|
+
return buildCompositionArray(counts, contextInfo.totalTokens);
|
|
23
|
+
}
|
|
24
|
+
// System prompt(s)
|
|
25
|
+
if (rawBody.system) {
|
|
26
|
+
if (typeof rawBody.system === "string") {
|
|
27
|
+
add("system_prompt", estimateTokens(rawBody.system));
|
|
28
|
+
}
|
|
29
|
+
else if (Array.isArray(rawBody.system)) {
|
|
30
|
+
for (const block of rawBody.system) {
|
|
31
|
+
// cache_control metadata is negligible; count only the text content
|
|
32
|
+
add("system_prompt", estimateTokens(block.text || block));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Instructions (OpenAI Responses API / ChatGPT)
|
|
37
|
+
if (rawBody.instructions) {
|
|
38
|
+
add("system_prompt", estimateTokens(rawBody.instructions));
|
|
39
|
+
}
|
|
40
|
+
// Tool definitions
|
|
41
|
+
if (rawBody.tools && Array.isArray(rawBody.tools)) {
|
|
42
|
+
add("tool_definitions", estimateTokens(JSON.stringify(rawBody.tools)));
|
|
43
|
+
}
|
|
44
|
+
// Gemini/Code Assist: unwrap .request wrapper if present
|
|
45
|
+
const geminiBody = rawBody.request || rawBody;
|
|
46
|
+
// Gemini systemInstruction
|
|
47
|
+
if (geminiBody.systemInstruction) {
|
|
48
|
+
const parts = geminiBody.systemInstruction.parts || [];
|
|
49
|
+
add("system_prompt", estimateTokens(parts.map((p) => p.text || "").join("\n")));
|
|
50
|
+
}
|
|
51
|
+
// Gemini contents[] or standard messages[]/input[]
|
|
52
|
+
const messages = geminiBody.contents || rawBody.messages || rawBody.input;
|
|
53
|
+
if (Array.isArray(messages)) {
|
|
54
|
+
for (const msg of messages) {
|
|
55
|
+
classifyMessage(msg, add);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else if (typeof messages === "string") {
|
|
59
|
+
add("user_text", estimateTokens(messages));
|
|
60
|
+
}
|
|
61
|
+
const total = Array.from(counts.values()).reduce((s, e) => s + e.tokens, 0);
|
|
62
|
+
return buildCompositionArray(counts, total);
|
|
63
|
+
}
|
|
64
|
+
function classifyMessage(msg, add) {
|
|
65
|
+
const type = msg.type || "";
|
|
66
|
+
// OpenAI Responses API typed items (no role field)
|
|
67
|
+
if (!msg.role && type) {
|
|
68
|
+
if (type === "function_call" || type === "custom_tool_call") {
|
|
69
|
+
add("tool_calls", estimateTokens(msg));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (type === "function_call_output" || type === "custom_tool_call_output") {
|
|
73
|
+
add("tool_results", estimateTokens(msg.output || ""));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (type === "reasoning") {
|
|
77
|
+
add("thinking", estimateTokens(msg));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (type === "output_text") {
|
|
81
|
+
add("assistant_text", estimateTokens(msg.text || ""));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (type === "input_text") {
|
|
85
|
+
add("user_text", estimateTokens(msg.text || ""));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const role = msg.role || "user";
|
|
90
|
+
const content = msg.content;
|
|
91
|
+
// System / developer messages
|
|
92
|
+
if (role === "system" || role === "developer") {
|
|
93
|
+
add("system_prompt", estimateTokens(content));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// String content
|
|
97
|
+
if (typeof content === "string") {
|
|
98
|
+
if (content.includes("<system-reminder>")) {
|
|
99
|
+
add("system_injections", estimateTokens(content));
|
|
100
|
+
}
|
|
101
|
+
else if (role === "assistant") {
|
|
102
|
+
add("assistant_text", estimateTokens(content));
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
add("user_text", estimateTokens(content));
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// Gemini parts array (role + parts instead of role + content)
|
|
110
|
+
if (msg.parts && Array.isArray(msg.parts)) {
|
|
111
|
+
for (const part of msg.parts) {
|
|
112
|
+
classifyGeminiPart(part, role, add);
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Array of content blocks
|
|
117
|
+
if (Array.isArray(content)) {
|
|
118
|
+
for (const block of content) {
|
|
119
|
+
classifyBlock(block, role, add);
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// Fallback
|
|
124
|
+
if (content) {
|
|
125
|
+
add("other", estimateTokens(content));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function classifyBlock(block, role, add) {
|
|
129
|
+
const type = block.type || "";
|
|
130
|
+
if (type === "tool_use") {
|
|
131
|
+
add("tool_calls", estimateTokens(block));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (type === "tool_result") {
|
|
135
|
+
add("tool_results", estimateTokens(block.content || ""));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (type === "thinking") {
|
|
139
|
+
add("thinking", estimateTokens(block.thinking || block.text || ""));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (type === "image" || type === "image_url") {
|
|
143
|
+
add("images", estimateTokens(block)); // estimateTokens handles image blocks with fixed estimate
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
// Text blocks
|
|
147
|
+
const text = block.text || "";
|
|
148
|
+
if (type === "text" || type === "input_text" || !type) {
|
|
149
|
+
if (text.includes("<system-reminder>")) {
|
|
150
|
+
add("system_injections", estimateTokens(text));
|
|
151
|
+
}
|
|
152
|
+
else if (block.cache_control) {
|
|
153
|
+
// Count text in its natural category only; the cache_control metadata
|
|
154
|
+
// itself is negligible overhead and should not inflate the total.
|
|
155
|
+
if (role === "assistant") {
|
|
156
|
+
add("assistant_text", estimateTokens(text));
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
add("user_text", estimateTokens(text));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else if (role === "assistant") {
|
|
163
|
+
add("assistant_text", estimateTokens(text));
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
add("user_text", estimateTokens(text));
|
|
167
|
+
}
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
add("other", estimateTokens(block));
|
|
171
|
+
}
|
|
172
|
+
function classifyGeminiPart(part, role, add) {
|
|
173
|
+
if (part.text) {
|
|
174
|
+
if (role === "model") {
|
|
175
|
+
add("assistant_text", estimateTokens(part.text));
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
add("user_text", estimateTokens(part.text));
|
|
179
|
+
}
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (part.functionCall) {
|
|
183
|
+
add("tool_calls", estimateTokens(part.functionCall));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (part.functionResponse) {
|
|
187
|
+
add("tool_results", estimateTokens(part.functionResponse));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (part.inlineData || part.fileData) {
|
|
191
|
+
add("images", estimateTokens(part));
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (part.executableCode || part.codeExecutionResult) {
|
|
195
|
+
add("assistant_text", estimateTokens(part));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
add("other", estimateTokens(part));
|
|
199
|
+
}
|
|
200
|
+
function buildCompositionArray(counts, total) {
|
|
201
|
+
const result = [];
|
|
202
|
+
for (const [category, { tokens, count }] of counts) {
|
|
203
|
+
if (tokens === 0)
|
|
204
|
+
continue;
|
|
205
|
+
result.push({
|
|
206
|
+
category,
|
|
207
|
+
tokens,
|
|
208
|
+
pct: total > 0 ? Math.round((tokens / total) * 1000) / 10 : 0,
|
|
209
|
+
count,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// Sort by tokens descending
|
|
213
|
+
result.sort((a, b) => b.tokens - a.tokens);
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Normalize composition token counts so their sum equals an authoritative total.
|
|
218
|
+
*
|
|
219
|
+
* Scales each entry proportionally and applies a rounding residual fix on the
|
|
220
|
+
* largest entry so `sum(composition[].tokens) === authoritative` exactly.
|
|
221
|
+
* Also recomputes `pct` fields.
|
|
222
|
+
*/
|
|
223
|
+
export function normalizeComposition(composition, authoritative) {
|
|
224
|
+
if (composition.length === 0)
|
|
225
|
+
return;
|
|
226
|
+
const rawSum = composition.reduce((s, c) => s + c.tokens, 0);
|
|
227
|
+
if (rawSum === 0 || authoritative === 0)
|
|
228
|
+
return;
|
|
229
|
+
if (rawSum === authoritative) {
|
|
230
|
+
// Already matches; just recompute pct for consistency
|
|
231
|
+
for (const c of composition) {
|
|
232
|
+
c.pct =
|
|
233
|
+
authoritative > 0
|
|
234
|
+
? Math.round((c.tokens / authoritative) * 1000) / 10
|
|
235
|
+
: 0;
|
|
236
|
+
}
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const scale = authoritative / rawSum;
|
|
240
|
+
let running = 0;
|
|
241
|
+
for (const c of composition) {
|
|
242
|
+
c.tokens = Math.round(c.tokens * scale);
|
|
243
|
+
running += c.tokens;
|
|
244
|
+
}
|
|
245
|
+
// Fix rounding residual on the first (largest) entry
|
|
246
|
+
const residual = authoritative - running;
|
|
247
|
+
if (residual !== 0) {
|
|
248
|
+
composition[0].tokens += residual;
|
|
249
|
+
}
|
|
250
|
+
// Recompute pct
|
|
251
|
+
for (const c of composition) {
|
|
252
|
+
c.pct =
|
|
253
|
+
authoritative > 0
|
|
254
|
+
? Math.round((c.tokens / authoritative) * 1000) / 10
|
|
255
|
+
: 0;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=composition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composition.js","sourceRoot":"","sources":["../../src/lhar/composition.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { LharJsonWrapper } from "../lhar-types.generated.js";
|
|
2
|
+
import type { CapturedEntry, Conversation, PrivacyLevel } from "../types.js";
|
|
3
|
+
export declare function toLharJsonl(entries: CapturedEntry[], conversations: Map<string, Conversation>, privacy?: PrivacyLevel): string;
|
|
4
|
+
export declare function toLharJson(entries: CapturedEntry[], conversations: Map<string, Conversation>, privacy?: PrivacyLevel): LharJsonWrapper;
|
|
5
|
+
//# sourceMappingURL=export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/lhar/export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAY7E,wBAAgB,WAAW,CACzB,OAAO,EAAE,aAAa,EAAE,EACxB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EACxC,OAAO,GAAE,YAAyB,GACjC,MAAM,CAmCR;AAED,wBAAgB,UAAU,CACxB,OAAO,EAAE,aAAa,EAAE,EACxB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EACxC,OAAO,GAAE,YAAyB,GACjC,eAAe,CAwCjB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { VERSION } from "../version.generated.js";
|
|
2
|
+
import { buildLharRecord, buildSessionLine, traceIdFromConversation, } from "./record.js";
|
|
3
|
+
const COLLECTOR_NAME = "context-lens";
|
|
4
|
+
const COLLECTOR_VERSION = VERSION;
|
|
5
|
+
const LHAR_VERSION = "0.1.0";
|
|
6
|
+
export function toLharJsonl(entries, conversations, privacy = "standard") {
|
|
7
|
+
// Sort oldest-first for JSONL
|
|
8
|
+
const sorted = [...entries].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
9
|
+
const lines = [];
|
|
10
|
+
const emittedSessions = new Set();
|
|
11
|
+
for (const entry of sorted) {
|
|
12
|
+
const record = buildLharRecord(entry, entries, privacy);
|
|
13
|
+
// Emit session preamble on first occurrence of each trace_id
|
|
14
|
+
if (!emittedSessions.has(record.trace_id)) {
|
|
15
|
+
emittedSessions.add(record.trace_id);
|
|
16
|
+
const convo = entry.conversationId
|
|
17
|
+
? conversations.get(entry.conversationId)
|
|
18
|
+
: undefined;
|
|
19
|
+
if (convo) {
|
|
20
|
+
lines.push(JSON.stringify(buildSessionLine(entry.conversationId, convo, record.gen_ai.request.model)));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
lines.push(JSON.stringify(record));
|
|
24
|
+
}
|
|
25
|
+
return `${lines.join("\n")}\n`;
|
|
26
|
+
}
|
|
27
|
+
export function toLharJson(entries, conversations, privacy = "standard") {
|
|
28
|
+
const sorted = [...entries].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
29
|
+
const records = sorted.map((entry) => buildLharRecord(entry, entries, privacy));
|
|
30
|
+
// Build sessions from conversations map
|
|
31
|
+
const sessions = [];
|
|
32
|
+
const seenTraces = new Set();
|
|
33
|
+
for (const record of records) {
|
|
34
|
+
if (!seenTraces.has(record.trace_id)) {
|
|
35
|
+
seenTraces.add(record.trace_id);
|
|
36
|
+
const convo = record.trace_id
|
|
37
|
+
? Array.from(conversations.values()).find((c) => traceIdFromConversation(c.id) === record.trace_id)
|
|
38
|
+
: undefined;
|
|
39
|
+
sessions.push({
|
|
40
|
+
trace_id: record.trace_id,
|
|
41
|
+
started_at: convo?.firstSeen || record.timestamp,
|
|
42
|
+
tool: record.source.tool,
|
|
43
|
+
model: record.gen_ai.request.model,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
lhar: {
|
|
49
|
+
version: LHAR_VERSION,
|
|
50
|
+
creator: {
|
|
51
|
+
name: COLLECTOR_NAME,
|
|
52
|
+
version: COLLECTOR_VERSION,
|
|
53
|
+
},
|
|
54
|
+
sessions,
|
|
55
|
+
entries: records,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/lhar/export.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { LharRecord, LharSessionLine } from "../lhar-types.generated.js";
|
|
2
|
+
import type { CapturedEntry, Conversation, PrivacyLevel } from "../types.js";
|
|
3
|
+
export declare function traceIdFromConversation(conversationId: string | null): string;
|
|
4
|
+
export declare function buildLharRecord(entry: CapturedEntry, prevEntries: CapturedEntry[], privacy?: PrivacyLevel): LharRecord;
|
|
5
|
+
export declare function buildSessionLine(conversationId: string, conversation: Conversation, model: string): LharSessionLine;
|
|
6
|
+
//# sourceMappingURL=record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/lhar/record.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAY7E,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAI7E;AA2BD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,aAAa,EACpB,WAAW,EAAE,aAAa,EAAE,EAC5B,OAAO,GAAE,YAAyB,GACjC,UAAU,CAqMZ;AAED,wBAAgB,gBAAgB,CAC9B,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,YAAY,EAC1B,KAAK,EAAE,MAAM,GACZ,eAAe,CAQjB"}
|