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.
Files changed (95) hide show
  1. package/README.md +63 -13
  2. package/dist/cli-utils.d.ts +1 -0
  3. package/dist/cli-utils.d.ts.map +1 -1
  4. package/dist/cli-utils.js +12 -0
  5. package/dist/cli-utils.js.map +1 -1
  6. package/dist/cli.js +110 -6
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/conversation.d.ts +10 -1
  9. package/dist/core/conversation.d.ts.map +1 -1
  10. package/dist/core/conversation.js +118 -1
  11. package/dist/core/conversation.js.map +1 -1
  12. package/dist/core/health.d.ts +7 -0
  13. package/dist/core/health.d.ts.map +1 -0
  14. package/dist/core/health.js +311 -0
  15. package/dist/core/health.js.map +1 -0
  16. package/dist/core/models.d.ts +8 -2
  17. package/dist/core/models.d.ts.map +1 -1
  18. package/dist/core/models.js +18 -3
  19. package/dist/core/models.js.map +1 -1
  20. package/dist/core/security.d.ts +8 -0
  21. package/dist/core/security.d.ts.map +1 -0
  22. package/dist/core/security.js +222 -0
  23. package/dist/core/security.js.map +1 -0
  24. package/dist/core/tokens.d.ts +20 -2
  25. package/dist/core/tokens.d.ts.map +1 -1
  26. package/dist/core/tokens.js +151 -4
  27. package/dist/core/tokens.js.map +1 -1
  28. package/dist/core.d.ts +4 -2
  29. package/dist/core.d.ts.map +1 -1
  30. package/dist/core.js +4 -2
  31. package/dist/core.js.map +1 -1
  32. package/dist/lhar/composition.d.ts +12 -0
  33. package/dist/lhar/composition.d.ts.map +1 -0
  34. package/dist/lhar/composition.js +258 -0
  35. package/dist/lhar/composition.js.map +1 -0
  36. package/dist/lhar/export.d.ts +5 -0
  37. package/dist/lhar/export.d.ts.map +1 -0
  38. package/dist/lhar/export.js +59 -0
  39. package/dist/lhar/export.js.map +1 -0
  40. package/dist/lhar/record.d.ts +6 -0
  41. package/dist/lhar/record.d.ts.map +1 -0
  42. package/dist/lhar/record.js +216 -0
  43. package/dist/lhar/record.js.map +1 -0
  44. package/dist/lhar/response.d.ts +11 -0
  45. package/dist/lhar/response.d.ts.map +1 -0
  46. package/dist/lhar/response.js +132 -0
  47. package/dist/lhar/response.js.map +1 -0
  48. package/dist/lhar-types.generated.d.ts +23 -2
  49. package/dist/lhar-types.generated.d.ts.map +1 -1
  50. package/dist/lhar.d.ts +12 -19
  51. package/dist/lhar.d.ts.map +1 -1
  52. package/dist/lhar.js +16 -557
  53. package/dist/lhar.js.map +1 -1
  54. package/dist/server/api.d.ts +8 -0
  55. package/dist/server/api.d.ts.map +1 -0
  56. package/dist/server/api.js +292 -0
  57. package/dist/server/api.js.map +1 -0
  58. package/dist/server/config.d.ts +2 -1
  59. package/dist/server/config.d.ts.map +1 -1
  60. package/dist/server/config.js +4 -1
  61. package/dist/server/config.js.map +1 -1
  62. package/dist/server/projection.d.ts.map +1 -1
  63. package/dist/server/projection.js +17 -9
  64. package/dist/server/projection.js.map +1 -1
  65. package/dist/server/proxy.d.ts.map +1 -1
  66. package/dist/server/proxy.js +36 -50
  67. package/dist/server/proxy.js.map +1 -1
  68. package/dist/server/static.d.ts +9 -0
  69. package/dist/server/static.d.ts.map +1 -0
  70. package/dist/server/static.js +78 -0
  71. package/dist/server/static.js.map +1 -0
  72. package/dist/server/store.d.ts +49 -1
  73. package/dist/server/store.d.ts.map +1 -1
  74. package/dist/server/store.js +297 -15
  75. package/dist/server/store.js.map +1 -1
  76. package/dist/server/webui.d.ts +2 -2
  77. package/dist/server/webui.d.ts.map +1 -1
  78. package/dist/server/webui.js +32 -160
  79. package/dist/server/webui.js.map +1 -1
  80. package/dist/server.js +3 -2
  81. package/dist/server.js.map +1 -1
  82. package/dist/types.d.ts +44 -0
  83. package/dist/types.d.ts.map +1 -1
  84. package/dist/version.generated.d.ts +1 -1
  85. package/dist/version.generated.js +1 -1
  86. package/findings-screenshot.png +0 -0
  87. package/messages-screenshot.png +0 -0
  88. package/package.json +9 -8
  89. package/schema/lhar.schema.json +57 -3
  90. package/screenshot-overview.png +0 -0
  91. package/sessions-screenshot.png +0 -0
  92. package/timeline-screenshot.png +0 -0
  93. package/diff.png +0 -0
  94. package/overview-sidebar.png +0 -0
  95. package/public/index.html +0 -2831
@@ -1,11 +1,29 @@
1
1
  /**
2
2
  * Lightweight token estimator used throughout Context Lens.
3
3
  *
4
- * This is intentionally simple and model-agnostic: we approximate tokens as `ceil(chars / 4)`.
5
- * It is good enough for rough context utilization and relative comparisons.
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":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAIpD"}
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"}
@@ -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
- * This is intentionally simple and model-agnostic: we approximate tokens as `ceil(chars / 4)`.
5
- * It is good enough for rough context utilization and relative comparisons.
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
- const s = typeof text === "object" ? JSON.stringify(text) : String(text);
14
- return Math.ceil(s.length / 4);
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
@@ -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;IACpB,MAAM,CAAC,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACzE,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACjC,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
@@ -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"}