context-lens 0.2.0 → 0.2.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.
Files changed (79) hide show
  1. package/README.md +62 -17
  2. package/dist/cli-utils.d.ts +1 -1
  3. package/dist/cli-utils.d.ts.map +1 -1
  4. package/dist/cli-utils.js +28 -13
  5. package/dist/cli-utils.js.map +1 -1
  6. package/dist/cli.js +77 -65
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/conversation.d.ts +54 -0
  9. package/dist/core/conversation.d.ts.map +1 -0
  10. package/dist/core/conversation.js +188 -0
  11. package/dist/core/conversation.js.map +1 -0
  12. package/dist/core/models.d.ts +30 -0
  13. package/dist/core/models.d.ts.map +1 -0
  14. package/dist/core/models.js +96 -0
  15. package/dist/core/models.js.map +1 -0
  16. package/dist/core/parse.d.ts +17 -0
  17. package/dist/core/parse.d.ts.map +1 -0
  18. package/dist/core/parse.js +349 -0
  19. package/dist/core/parse.js.map +1 -0
  20. package/dist/core/routing.d.ts +47 -0
  21. package/dist/core/routing.d.ts.map +1 -0
  22. package/dist/core/routing.js +132 -0
  23. package/dist/core/routing.js.map +1 -0
  24. package/dist/core/source.d.ts +22 -0
  25. package/dist/core/source.d.ts.map +1 -0
  26. package/dist/core/source.js +56 -0
  27. package/dist/core/source.js.map +1 -0
  28. package/dist/core/tokens.d.ts +11 -0
  29. package/dist/core/tokens.d.ts.map +1 -0
  30. package/dist/core/tokens.js +16 -0
  31. package/dist/core/tokens.js.map +1 -0
  32. package/dist/core.d.ts +12 -22
  33. package/dist/core.d.ts.map +1 -1
  34. package/dist/core.js +12 -471
  35. package/dist/core.js.map +1 -1
  36. package/dist/http/headers.d.ts +25 -0
  37. package/dist/http/headers.d.ts.map +1 -0
  38. package/dist/http/headers.js +54 -0
  39. package/dist/http/headers.js.map +1 -0
  40. package/dist/lhar-types.generated.d.ts +1 -1
  41. package/dist/lhar.d.ts +4 -4
  42. package/dist/lhar.d.ts.map +1 -1
  43. package/dist/lhar.js +190 -106
  44. package/dist/lhar.js.map +1 -1
  45. package/dist/server/config.d.ts +12 -0
  46. package/dist/server/config.d.ts.map +1 -0
  47. package/dist/server/config.js +33 -0
  48. package/dist/server/config.js.map +1 -0
  49. package/dist/server/projection.d.ts +9 -0
  50. package/dist/server/projection.d.ts.map +1 -0
  51. package/dist/server/projection.js +39 -0
  52. package/dist/server/projection.js.map +1 -0
  53. package/dist/server/proxy.d.ts +13 -0
  54. package/dist/server/proxy.d.ts.map +1 -0
  55. package/dist/server/proxy.js +232 -0
  56. package/dist/server/proxy.js.map +1 -0
  57. package/dist/server/store.d.ts +33 -0
  58. package/dist/server/store.d.ts.map +1 -0
  59. package/dist/server/store.js +350 -0
  60. package/dist/server/store.js.map +1 -0
  61. package/dist/server/webui.d.ts +5 -0
  62. package/dist/server/webui.d.ts.map +1 -0
  63. package/dist/server/webui.js +170 -0
  64. package/dist/server/webui.js.map +1 -0
  65. package/dist/server-utils.d.ts +2 -2
  66. package/dist/server-utils.d.ts.map +1 -1
  67. package/dist/server-utils.js +12 -21
  68. package/dist/server-utils.js.map +1 -1
  69. package/dist/server.js +30 -697
  70. package/dist/server.js.map +1 -1
  71. package/dist/types.d.ts +50 -10
  72. package/dist/types.d.ts.map +1 -1
  73. package/dist/version.generated.d.ts +2 -0
  74. package/dist/version.generated.d.ts.map +1 -0
  75. package/dist/version.generated.js +2 -0
  76. package/dist/version.generated.js.map +1 -0
  77. package/package.json +18 -6
  78. package/public/index.html +39 -12
  79. package/schema/lhar.schema.json +1 -1
@@ -0,0 +1,54 @@
1
+ import type { ContextInfo, ParsedMessage } from "../types.js";
2
+ /**
3
+ * Extract readable text from message content.
4
+ *
5
+ * This is used for labels/fingerprints where we want the "real" prompt text, not wrappers.
6
+ * It understands a few common JSON wrappers used by tool APIs.
7
+ *
8
+ * @param content - Raw message content (string, often JSON-encoded).
9
+ * @returns A trimmed text string or `null` if no readable text exists.
10
+ */
11
+ export declare function extractReadableText(content: string | null | undefined): string | null;
12
+ /**
13
+ * Extract the working directory from captured content when a tool embeds it.
14
+ *
15
+ * Supports:
16
+ * - Claude Code: "Primary working directory: `/path`"
17
+ * - Codex: "<cwd>/path</cwd>"
18
+ * - Generic: "working directory is /path" or "cwd: /path"
19
+ */
20
+ export declare function extractWorkingDirectory(contextInfo: ContextInfo): string | null;
21
+ /**
22
+ * For OpenAI Responses-style input arrays, extract the first "real" user prompt.
23
+ *
24
+ * Skips boilerplate blocks (AGENTS.md / environment wrappers).
25
+ */
26
+ export declare function extractUserPrompt(messages: ParsedMessage[]): string | null;
27
+ /**
28
+ * Extract a stable session identifier when the upstream/tool provides one.
29
+ *
30
+ * Supported:
31
+ * - Anthropic: `metadata.user_id` contains `session_<uuid>`
32
+ * - Gemini Code Assist: `request.session_id` (uuid)
33
+ */
34
+ export declare function extractSessionId(rawBody: Record<string, any> | null | undefined): string | null;
35
+ /**
36
+ * Compute a sub-key to distinguish agents within a session (main vs subagents).
37
+ *
38
+ * Currently derived from the first readable user message.
39
+ */
40
+ export declare function computeAgentKey(contextInfo: ContextInfo): string | null;
41
+ /**
42
+ * Compute a conversation fingerprint for grouping.
43
+ *
44
+ * Priority:
45
+ * 1. explicit session IDs (most stable)
46
+ * 2. Responses API chaining (`previous_response_id`)
47
+ * 3. content hash of system + first prompt
48
+ */
49
+ export declare function computeFingerprint(contextInfo: ContextInfo, rawBody: Record<string, any> | null | undefined, responseIdToConvo: Map<string, string>): string | null;
50
+ /**
51
+ * Extract a readable label for a conversation, primarily for UI display.
52
+ */
53
+ export declare function extractConversationLabel(contextInfo: ContextInfo): string;
54
+ //# sourceMappingURL=conversation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/core/conversation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE9D;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACjC,MAAM,GAAG,IAAI,CAuBf;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,WAAW,GACvB,MAAM,GAAG,IAAI,CAwBf;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,MAAM,GAAG,IAAI,CAmB1E;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,GAC9C,MAAM,GAAG,IAAI,CAUf;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAcvE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,EAC/C,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC,MAAM,GAAG,IAAI,CA+Bf;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CAgBzE"}
@@ -0,0 +1,188 @@
1
+ import { createHash } from "node:crypto";
2
+ /**
3
+ * Extract readable text from message content.
4
+ *
5
+ * This is used for labels/fingerprints where we want the "real" prompt text, not wrappers.
6
+ * It understands a few common JSON wrappers used by tool APIs.
7
+ *
8
+ * @param content - Raw message content (string, often JSON-encoded).
9
+ * @returns A trimmed text string or `null` if no readable text exists.
10
+ */
11
+ export function extractReadableText(content) {
12
+ if (!content)
13
+ return null;
14
+ let text = content;
15
+ try {
16
+ const parsed = JSON.parse(text);
17
+ if (Array.isArray(parsed)) {
18
+ const textBlock = parsed.find((b) => (b.type === "text" &&
19
+ b.text &&
20
+ !b.text.startsWith("<system-reminder>")) ||
21
+ (b.type === "input_text" &&
22
+ b.text &&
23
+ !b.text.startsWith("#") &&
24
+ !b.text.startsWith("<environment")));
25
+ if (textBlock)
26
+ text = textBlock.text;
27
+ }
28
+ }
29
+ catch {
30
+ // Expected: input may not be valid JSON; fall through to raw text
31
+ }
32
+ text = text.replace(/\s+/g, " ").trim();
33
+ return text || null;
34
+ }
35
+ /**
36
+ * Extract the working directory from captured content when a tool embeds it.
37
+ *
38
+ * Supports:
39
+ * - Claude Code: "Primary working directory: `/path`"
40
+ * - Codex: "<cwd>/path</cwd>"
41
+ * - Generic: "working directory is /path" or "cwd: /path"
42
+ */
43
+ export function extractWorkingDirectory(contextInfo) {
44
+ const allText = [
45
+ ...(contextInfo.systemPrompts || []).map((sp) => sp.content),
46
+ ...(contextInfo.messages || [])
47
+ .filter((m) => m.role === "user")
48
+ .map((m) => m.content),
49
+ ].join("\n");
50
+ let match = allText.match(/[Pp]rimary working directory[:\s]+[`]?([/~][^\s`\n]+)/);
51
+ if (match)
52
+ return match[1];
53
+ match = allText.match(/<cwd>([^<]+)<\/cwd>/);
54
+ if (match)
55
+ return match[1];
56
+ // Generic: "working directory is /path" or "working directory = /path"
57
+ match = allText.match(/working directory (?:is |= ?)[`"]?([/~][^\s`"'\n]+)/i);
58
+ if (match)
59
+ return match[1];
60
+ // Generic: "working directory: /path" or "working directory /path"
61
+ match = allText.match(/working directory[:\s]+[`"]?([/~][^\s`"'\n]+)/i);
62
+ if (match)
63
+ return match[1];
64
+ match = allText.match(/\bcwd[:\s]+[`"]?([/~][^\s`"'\n]+)/);
65
+ if (match)
66
+ return match[1];
67
+ return null;
68
+ }
69
+ /**
70
+ * For OpenAI Responses-style input arrays, extract the first "real" user prompt.
71
+ *
72
+ * Skips boilerplate blocks (AGENTS.md / environment wrappers).
73
+ */
74
+ export function extractUserPrompt(messages) {
75
+ for (const m of messages) {
76
+ if (m.role !== "user" || !m.content)
77
+ continue;
78
+ try {
79
+ const parsed = JSON.parse(m.content);
80
+ if (Array.isArray(parsed) &&
81
+ parsed[0] &&
82
+ parsed[0].type === "input_text") {
83
+ const text = parsed[0].text || "";
84
+ if (text.startsWith("#") || text.startsWith("<environment"))
85
+ continue;
86
+ return m.content;
87
+ }
88
+ }
89
+ catch {
90
+ // Expected: content may not be valid JSON; skip this message
91
+ }
92
+ }
93
+ return null;
94
+ }
95
+ /**
96
+ * Extract a stable session identifier when the upstream/tool provides one.
97
+ *
98
+ * Supported:
99
+ * - Anthropic: `metadata.user_id` contains `session_<uuid>`
100
+ * - Gemini Code Assist: `request.session_id` (uuid)
101
+ */
102
+ export function extractSessionId(rawBody) {
103
+ const userId = rawBody?.metadata?.user_id;
104
+ if (userId) {
105
+ const match = userId.match(/session_([a-f0-9-]+)/);
106
+ if (match)
107
+ return match[0];
108
+ }
109
+ const geminiSessionId = rawBody?.request?.session_id;
110
+ if (geminiSessionId && typeof geminiSessionId === "string")
111
+ return `gemini_${geminiSessionId}`;
112
+ return null;
113
+ }
114
+ /**
115
+ * Compute a sub-key to distinguish agents within a session (main vs subagents).
116
+ *
117
+ * Currently derived from the first readable user message.
118
+ */
119
+ export function computeAgentKey(contextInfo) {
120
+ const userMsgs = (contextInfo.messages || []).filter((m) => m.role === "user");
121
+ let realText = "";
122
+ for (const msg of userMsgs) {
123
+ const t = extractReadableText(msg.content);
124
+ if (t) {
125
+ realText = t;
126
+ break;
127
+ }
128
+ }
129
+ if (!realText)
130
+ return null;
131
+ return createHash("sha256").update(realText).digest("hex").slice(0, 12);
132
+ }
133
+ /**
134
+ * Compute a conversation fingerprint for grouping.
135
+ *
136
+ * Priority:
137
+ * 1. explicit session IDs (most stable)
138
+ * 2. Responses API chaining (`previous_response_id`)
139
+ * 3. content hash of system + first prompt
140
+ */
141
+ export function computeFingerprint(contextInfo, rawBody, responseIdToConvo) {
142
+ const sessionId = extractSessionId(rawBody);
143
+ if (sessionId) {
144
+ return createHash("sha256").update(sessionId).digest("hex").slice(0, 16);
145
+ }
146
+ if (rawBody?.previous_response_id && responseIdToConvo) {
147
+ const existing = responseIdToConvo.get(rawBody.previous_response_id);
148
+ if (existing)
149
+ return existing;
150
+ }
151
+ const userMsgs = (contextInfo.messages || []).filter((m) => m.role === "user");
152
+ let promptText;
153
+ if (contextInfo.apiFormat === "responses" && userMsgs.length > 1) {
154
+ promptText = extractUserPrompt(userMsgs) || "";
155
+ }
156
+ else {
157
+ const firstUser = userMsgs[0];
158
+ promptText = firstUser ? firstUser.content : "";
159
+ }
160
+ const systemText = (contextInfo.systemPrompts || [])
161
+ .map((sp) => sp.content)
162
+ .join("\n");
163
+ if (!systemText && !promptText)
164
+ return null;
165
+ return createHash("sha256")
166
+ .update(`${systemText}\0${promptText}`)
167
+ .digest("hex")
168
+ .slice(0, 16);
169
+ }
170
+ /**
171
+ * Extract a readable label for a conversation, primarily for UI display.
172
+ */
173
+ export function extractConversationLabel(contextInfo) {
174
+ const userMsgs = (contextInfo.messages || []).filter((m) => m.role === "user");
175
+ if (contextInfo.apiFormat === "responses" && userMsgs.length > 1) {
176
+ const prompt = extractUserPrompt(userMsgs);
177
+ const text = extractReadableText(prompt);
178
+ if (text)
179
+ return text.length > 80 ? `${text.slice(0, 77)}...` : text;
180
+ }
181
+ for (let i = userMsgs.length - 1; i >= 0; i--) {
182
+ const text = extractReadableText(userMsgs[i].content);
183
+ if (text)
184
+ return text.length > 80 ? `${text.slice(0, 77)}...` : text;
185
+ }
186
+ return "Unnamed conversation";
187
+ }
188
+ //# sourceMappingURL=conversation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation.js","sourceRoot":"","sources":["../../src/core/conversation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Known model context limits (tokens).
3
+ *
4
+ * Keys are ordered most-specific-first because `getContextLimit()` does substring matching.
5
+ */
6
+ export declare const CONTEXT_LIMITS: Record<string, number>;
7
+ /**
8
+ * Resolve an approximate context window size for a given model string.
9
+ *
10
+ * @param model - Model identifier (often includes version suffixes).
11
+ * @returns Context limit in tokens.
12
+ */
13
+ export declare function getContextLimit(model: string): number;
14
+ /**
15
+ * Model pricing: `[inputPerMTok, outputPerMTok]` in USD.
16
+ *
17
+ * Keys ordered most-specific-first to avoid substring false matches
18
+ * (e.g. `gpt-4o-mini` before `gpt-4o`).
19
+ */
20
+ export declare const MODEL_PRICING: Record<string, [number, number]>;
21
+ /**
22
+ * Estimate cost in USD for a request/response token pair using `MODEL_PRICING`.
23
+ *
24
+ * @param model - Model identifier (substring matched against known keys).
25
+ * @param inputTokens - Input/prompt tokens.
26
+ * @param outputTokens - Output/completion tokens.
27
+ * @returns Cost in USD, rounded to 6 decimals; `null` if the model is unknown.
28
+ */
29
+ export declare function estimateCost(model: string, inputTokens: number, outputTokens: number): number | null;
30
+ //# sourceMappingURL=models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/core/models.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAyBjD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CA4B1D,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACnB,MAAM,GAAG,IAAI,CAWf"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Known model context limits (tokens).
3
+ *
4
+ * Keys are ordered most-specific-first because `getContextLimit()` does substring matching.
5
+ */
6
+ export const CONTEXT_LIMITS = {
7
+ // Anthropic
8
+ "claude-opus-4": 200000,
9
+ "claude-sonnet-4": 200000,
10
+ "claude-haiku-4": 200000,
11
+ "claude-3-5-sonnet": 200000,
12
+ "claude-3-5-haiku": 200000,
13
+ "claude-3-opus": 200000,
14
+ // OpenAI (specific before generic)
15
+ "gpt-4o-mini": 128000,
16
+ "gpt-4o": 128000,
17
+ "gpt-4-turbo": 128000,
18
+ "gpt-4": 8192,
19
+ "gpt-3.5-turbo": 16385,
20
+ "o4-mini": 200000,
21
+ "o3-mini": 200000,
22
+ o3: 200000,
23
+ "o1-mini": 128000,
24
+ o1: 200000,
25
+ // Gemini
26
+ "gemini-2.5-pro": 1048576,
27
+ "gemini-2.5-flash": 1048576,
28
+ "gemini-2.0-flash": 1048576,
29
+ "gemini-1.5-pro": 2097152,
30
+ "gemini-1.5-flash": 1048576,
31
+ };
32
+ /**
33
+ * Resolve an approximate context window size for a given model string.
34
+ *
35
+ * @param model - Model identifier (often includes version suffixes).
36
+ * @returns Context limit in tokens.
37
+ */
38
+ export function getContextLimit(model) {
39
+ for (const [key, limit] of Object.entries(CONTEXT_LIMITS)) {
40
+ if (model.includes(key))
41
+ return limit;
42
+ }
43
+ return 128000; // default fallback
44
+ }
45
+ /**
46
+ * Model pricing: `[inputPerMTok, outputPerMTok]` in USD.
47
+ *
48
+ * Keys ordered most-specific-first to avoid substring false matches
49
+ * (e.g. `gpt-4o-mini` before `gpt-4o`).
50
+ */
51
+ export const MODEL_PRICING = {
52
+ "claude-opus-4": [15, 75],
53
+ "claude-sonnet-4": [3, 15],
54
+ "claude-haiku-4": [0.8, 4],
55
+ "claude-3-5-sonnet": [3, 15],
56
+ "claude-3-5-haiku": [0.8, 4],
57
+ "claude-3-opus": [15, 75],
58
+ "gpt-4o-mini": [0.15, 0.6],
59
+ "gpt-4o": [2.5, 10],
60
+ "gpt-4-turbo": [10, 30],
61
+ "gpt-4": [30, 60],
62
+ "o4-mini": [1.1, 4.4],
63
+ "o3-mini": [1.1, 4.4],
64
+ o3: [10, 40],
65
+ "o1-mini": [3, 12],
66
+ o1: [15, 60],
67
+ // Codex (subscription estimate)
68
+ "gpt-5.3-codex": [1.75, 14],
69
+ "gpt-5.2-codex": [1.75, 14],
70
+ "gpt-5.1-codex-mini": [0.25, 2],
71
+ "gpt-5.1-codex": [1.25, 10],
72
+ "gpt-5-codex": [1.25, 10],
73
+ // Gemini
74
+ "gemini-2.5-pro": [1.25, 10],
75
+ "gemini-2.5-flash": [0.15, 0.6],
76
+ "gemini-2.0-flash": [0.1, 0.4],
77
+ "gemini-1.5-pro": [1.25, 5],
78
+ "gemini-1.5-flash": [0.075, 0.3],
79
+ };
80
+ /**
81
+ * Estimate cost in USD for a request/response token pair using `MODEL_PRICING`.
82
+ *
83
+ * @param model - Model identifier (substring matched against known keys).
84
+ * @param inputTokens - Input/prompt tokens.
85
+ * @param outputTokens - Output/completion tokens.
86
+ * @returns Cost in USD, rounded to 6 decimals; `null` if the model is unknown.
87
+ */
88
+ export function estimateCost(model, inputTokens, outputTokens) {
89
+ for (const [key, [inp, out]] of Object.entries(MODEL_PRICING)) {
90
+ if (model.includes(key)) {
91
+ return (Math.round(((inputTokens * inp + outputTokens * out) / 1_000_000) * 1_000_000) / 1_000_000);
92
+ }
93
+ }
94
+ return null;
95
+ }
96
+ //# sourceMappingURL=models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/core/models.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAA2B;IACpD,YAAY;IACZ,eAAe,EAAE,MAAM;IACvB,iBAAiB,EAAE,MAAM;IACzB,gBAAgB,EAAE,MAAM;IACxB,mBAAmB,EAAE,MAAM;IAC3B,kBAAkB,EAAE,MAAM;IAC1B,eAAe,EAAE,MAAM;IACvB,mCAAmC;IACnC,aAAa,EAAE,MAAM;IACrB,QAAQ,EAAE,MAAM;IAChB,aAAa,EAAE,MAAM;IACrB,OAAO,EAAE,IAAI;IACb,eAAe,EAAE,KAAK;IACtB,SAAS,EAAE,MAAM;IACjB,SAAS,EAAE,MAAM;IACjB,EAAE,EAAE,MAAM;IACV,SAAS,EAAE,MAAM;IACjB,EAAE,EAAE,MAAM;IACV,SAAS;IACT,gBAAgB,EAAE,OAAO;IACzB,kBAAkB,EAAE,OAAO;IAC3B,kBAAkB,EAAE,OAAO;IAC3B,gBAAgB,EAAE,OAAO;IACzB,kBAAkB,EAAE,OAAO;CAC5B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QAC1D,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IACxC,CAAC;IACD,OAAO,MAAM,CAAC,CAAC,mBAAmB;AACpC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAqC;IAC7D,eAAe,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACzB,iBAAiB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;IAC1B,gBAAgB,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;IAC1B,mBAAmB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;IAC5B,kBAAkB,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;IAC5B,eAAe,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACzB,aAAa,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC;IAC1B,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;IACnB,aAAa,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACvB,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACjB,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;IACrB,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;IACrB,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACZ,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;IAClB,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACZ,gCAAgC;IAChC,eAAe,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;IAC3B,eAAe,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;IAC3B,oBAAoB,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/B,eAAe,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;IAC3B,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;IACzB,SAAS;IACT,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;IAC5B,kBAAkB,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC;IAC/B,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;IAC9B,gBAAgB,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3B,kBAAkB,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC;CACjC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAa,EACb,WAAmB,EACnB,YAAoB;IAEpB,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9D,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,CACL,IAAI,CAAC,KAAK,CACR,CAAC,CAAC,WAAW,GAAG,GAAG,GAAG,YAAY,GAAG,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS,CACnE,GAAG,SAAS,CACd,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { ContextInfo } from "../types.js";
2
+ /**
3
+ * Parse a request body and extract normalized context information.
4
+ *
5
+ * This is the core "shape-normalizer" for Context Lens. It supports:
6
+ * - Anthropic Messages API
7
+ * - OpenAI Responses API and Chat Completions
8
+ * - ChatGPT backend schema (Codex subscription traffic)
9
+ * - Gemini (including Code Assist wrappers)
10
+ *
11
+ * @param provider - Provider inferred from routing (anthropic/openai/gemini/chatgpt/unknown).
12
+ * @param body - Parsed JSON request body.
13
+ * @param apiFormat - API schema family inferred from routing.
14
+ * @returns A `ContextInfo` with token estimates and normalized messages/blocks.
15
+ */
16
+ export declare function parseContextInfo(provider: string, body: Record<string, any>, apiFormat: string): ContextInfo;
17
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/core/parse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,WAAW,EAAiB,MAAM,aAAa,CAAC;AA2J5E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,SAAS,EAAE,MAAM,GAChB,WAAW,CAmMb"}