@tonyclaw/agent-inspector 2.0.7 → 2.0.9

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 (33) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-BhRYPp6T.js → CompareDrawer-Cb4RH049.js} +1 -1
  3. package/.output/public/assets/{ProxyViewerContainer-BHq0dkQG.js → ProxyViewerContainer-Cmr2TANl.js} +5 -5
  4. package/.output/public/assets/{ReplayDialog-Ch0gwg6B.js → ReplayDialog-B3y8E7IE.js} +1 -1
  5. package/.output/public/assets/RequestAnatomy-C7BQYj95.js +1 -0
  6. package/.output/public/assets/{ResponseView-hRLar32x.js → ResponseView-Bylsp4WJ.js} +1 -1
  7. package/.output/public/assets/{StreamingChunkSequence-DCJ_gUHa.js → StreamingChunkSequence-CJH13VD3.js} +1 -1
  8. package/.output/public/assets/_sessionId-3e8W68GL.js +1 -0
  9. package/.output/public/assets/index-Bo1dGS4R.css +1 -0
  10. package/.output/public/assets/index-Cz5aqmzE.js +1 -0
  11. package/.output/public/assets/{main-BlDtK0Nn.js → main-Bww0Bc88.js} +8 -8
  12. package/.output/server/{_sessionId-Dy7rsLS4.mjs → _sessionId-BnCF8Ito.mjs} +2 -2
  13. package/.output/server/_ssr/{CompareDrawer-CkVX-ARV.mjs → CompareDrawer-jPU14EI3.mjs} +2 -2
  14. package/.output/server/_ssr/{ProxyViewerContainer-B5a9p3j5.mjs → ProxyViewerContainer-xesEFppM.mjs} +9 -8
  15. package/.output/server/_ssr/{ReplayDialog-hXY6H9fB.mjs → ReplayDialog-DQvW2zoW.mjs} +3 -3
  16. package/.output/server/_ssr/{RequestAnatomy-B6MS7VvA.mjs → RequestAnatomy-BilEphjP.mjs} +455 -3
  17. package/.output/server/_ssr/{ResponseView-ByjxwUnp.mjs → ResponseView-DB5PPcWf.mjs} +2 -2
  18. package/.output/server/_ssr/{StreamingChunkSequence-C5Kb8siT.mjs → StreamingChunkSequence-Dnrgn2AP.mjs} +2 -2
  19. package/.output/server/_ssr/{index-BZXtj42m.mjs → index-YPp821bH.mjs} +2 -2
  20. package/.output/server/_ssr/index.mjs +2 -2
  21. package/.output/server/_ssr/{router-CVQaVF_3.mjs → router-BvuK0vQv.mjs} +20 -4
  22. package/.output/server/{_tanstack-start-manifest_v-Bpe5ZC56.mjs → _tanstack-start-manifest_v-DGw3vVOm.mjs} +1 -1
  23. package/.output/server/index.mjs +60 -60
  24. package/bin/agent-inspector.js +2 -0
  25. package/package.json +3 -2
  26. package/src/components/proxy-viewer/LogEntry.tsx +2 -1
  27. package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +197 -1
  28. package/src/components/proxy-viewer/anatomy/contextIntelligence.ts +414 -0
  29. package/src/routes/__root.tsx +27 -0
  30. package/.output/public/assets/RequestAnatomy-CKbvIgaN.js +0 -1
  31. package/.output/public/assets/_sessionId-BJTz6t5l.js +0 -1
  32. package/.output/public/assets/index-Cf9U0Ekq.js +0 -1
  33. package/.output/public/assets/index-DTdv7nzl.css +0 -1
@@ -1,6 +1,6 @@
1
1
  import { j as jsxRuntimeExports } from "./_libs/react.mjs";
2
- import { P as ProxyViewerContainer } from "./_ssr/ProxyViewerContainer-B5a9p3j5.mjs";
3
- import { R as Route$p } from "./_ssr/router-CVQaVF_3.mjs";
2
+ import { P as ProxyViewerContainer } from "./_ssr/ProxyViewerContainer-xesEFppM.mjs";
3
+ import { R as Route$p } from "./_ssr/router-BvuK0vQv.mjs";
4
4
  import "./_libs/jszip.mjs";
5
5
  import "./_libs/modelcontextprotocol__server.mjs";
6
6
  import "./_libs/swr.mjs";
@@ -1,6 +1,6 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
2
- import { g as getLogFormatAdapter, r as resolveLogFormat, a as getConversationId, c as cn, B as Badge, f as formatTokens, J as JsonViewerFromString } from "./ProxyViewerContainer-B5a9p3j5.mjs";
3
- import "./router-CVQaVF_3.mjs";
2
+ import { g as getLogFormatAdapter, r as resolveLogFormat, a as getConversationId, c as cn, B as Badge, f as formatTokens, J as JsonViewerFromString } from "./ProxyViewerContainer-xesEFppM.mjs";
3
+ import "./router-BvuK0vQv.mjs";
4
4
  import "../_libs/modelcontextprotocol__server.mjs";
5
5
  import "../_libs/jszip.mjs";
6
6
  import { X, a7 as Rows3, a8 as Columns2, n as Minus, P as Plus, m as Pencil, f as ChevronRight, a9 as Equal, C as Check, a as Copy } from "../_libs/lucide-react.mjs";
@@ -1,5 +1,5 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
2
- import { C as CapturedLogSchema, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, a as DEFAULT_TIME_DISPLAY_FORMAT, b as RuntimeConfigSchema, r as requestFormatForPath, e as createPendingProviderTestResults, P as ProviderTestResultsSchema, f as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, T as TimeDisplayFormatSchema, g as getSessionPath, h as ProviderConfigSchema, K as KnowledgeCandidateSchema, d as stripClaudeCodeBillingHeader, s as safeGetOwnProperty, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, c as AnthropicRequestSchema } from "./router-CVQaVF_3.mjs";
2
+ import { C as CapturedLogSchema, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, a as DEFAULT_TIME_DISPLAY_FORMAT, b as RuntimeConfigSchema, r as requestFormatForPath, e as createPendingProviderTestResults, P as ProviderTestResultsSchema, f as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, T as TimeDisplayFormatSchema, g as getSessionPath, h as ProviderConfigSchema, K as KnowledgeCandidateSchema, d as stripClaudeCodeBillingHeader, s as safeGetOwnProperty, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, c as AnthropicRequestSchema } from "./router-BvuK0vQv.mjs";
3
3
  import { u as useSWR, a as useSWRConfig } from "../_libs/swr.mjs";
4
4
  import { J as JSZip } from "../_libs/jszip.mjs";
5
5
  import { c as clsx } from "../_libs/clsx.mjs";
@@ -298,7 +298,7 @@ function getStatusCategory(status) {
298
298
  if (status >= 500) return "server_error";
299
299
  return "pending";
300
300
  }
301
- const version = "2.0.7";
301
+ const version = "2.0.9";
302
302
  const packageJson = {
303
303
  version
304
304
  };
@@ -1359,19 +1359,19 @@ function useCopyFeedback(text) {
1359
1359
  return { copied, copy };
1360
1360
  }
1361
1361
  const LazyCompareDrawer = reactExports.lazy(
1362
- () => import("./CompareDrawer-CkVX-ARV.mjs").then((m) => ({ default: m.CompareDrawer }))
1362
+ () => import("./CompareDrawer-jPU14EI3.mjs").then((m) => ({ default: m.CompareDrawer }))
1363
1363
  );
1364
1364
  const LazyReplayDialog = reactExports.lazy(
1365
- () => import("./ReplayDialog-hXY6H9fB.mjs").then((m) => ({ default: m.ReplayDialog }))
1365
+ () => import("./ReplayDialog-DQvW2zoW.mjs").then((m) => ({ default: m.ReplayDialog }))
1366
1366
  );
1367
1367
  const LazyRequestAnatomy = reactExports.lazy(
1368
- () => import("./RequestAnatomy-B6MS7VvA.mjs").then((m) => ({ default: m.RequestAnatomy }))
1368
+ () => import("./RequestAnatomy-BilEphjP.mjs").then((m) => ({ default: m.RequestAnatomy }))
1369
1369
  );
1370
1370
  const LazyResponseView = reactExports.lazy(
1371
- () => import("./ResponseView-ByjxwUnp.mjs").then((m) => ({ default: m.ResponseView }))
1371
+ () => import("./ResponseView-DB5PPcWf.mjs").then((m) => ({ default: m.ResponseView }))
1372
1372
  );
1373
1373
  const LazyStreamingChunkSequence = reactExports.lazy(
1374
- () => import("./StreamingChunkSequence-C5Kb8siT.mjs").then((m) => ({
1374
+ () => import("./StreamingChunkSequence-Dnrgn2AP.mjs").then((m) => ({
1375
1375
  default: m.StreamingChunkSequence
1376
1376
  }))
1377
1377
  );
@@ -3421,8 +3421,9 @@ const LogEntry = reactExports.memo(function({
3421
3421
  anatomySegments !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "anatomy", children: activeTab === "anatomy" && /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3422
3422
  LazyRequestAnatomy,
3423
3423
  {
3424
- parsed: null,
3424
+ parsed: requestExpansion.parsedData,
3425
3425
  inputTokens: log.inputTokens ?? null,
3426
+ model: log.model,
3426
3427
  segments: anatomySegments,
3427
3428
  onSegmentActivate: jumpToAnatomySegment
3428
3429
  }
@@ -1,7 +1,7 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
2
- import { u as useProviders, D as Dialog, b as DialogContent, d as DialogHeader, e as DialogTitle, T as Tabs, h as TabsList, i as TabsTrigger, j as TabsContent, k as TooltipProvider, l as Tooltip, m as TooltipTrigger, n as TooltipContent, o as Button } from "./ProxyViewerContainer-B5a9p3j5.mjs";
3
- import { ResponseView } from "./ResponseView-ByjxwUnp.mjs";
4
- import "./router-CVQaVF_3.mjs";
2
+ import { u as useProviders, D as Dialog, b as DialogContent, d as DialogHeader, e as DialogTitle, T as Tabs, h as TabsList, i as TabsTrigger, j as TabsContent, k as TooltipProvider, l as Tooltip, m as TooltipTrigger, n as TooltipContent, o as Button } from "./ProxyViewerContainer-xesEFppM.mjs";
3
+ import { ResponseView } from "./ResponseView-DB5PPcWf.mjs";
4
+ import "./router-BvuK0vQv.mjs";
5
5
  import "../_libs/modelcontextprotocol__server.mjs";
6
6
  import "../_libs/jszip.mjs";
7
7
  import { I as RotateCcw } from "../_libs/lucide-react.mjs";
@@ -1,8 +1,8 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
2
- import { f as formatTokens, k as TooltipProvider, c as cn, l as Tooltip, m as TooltipTrigger, n as TooltipContent } from "./ProxyViewerContainer-B5a9p3j5.mjs";
3
- import "./router-CVQaVF_3.mjs";
4
- import "../_libs/modelcontextprotocol__server.mjs";
2
+ import { f as formatTokens, k as TooltipProvider, c as cn, l as Tooltip, m as TooltipTrigger, n as TooltipContent } from "./ProxyViewerContainer-xesEFppM.mjs";
3
+ import { s as safeGetOwnProperty } from "./router-BvuK0vQv.mjs";
5
4
  import "../_libs/jszip.mjs";
5
+ import "../_libs/modelcontextprotocol__server.mjs";
6
6
  import { aa as Info } from "../_libs/lucide-react.mjs";
7
7
  import "../_libs/swr.mjs";
8
8
  import "../_libs/use-sync-external-store.mjs";
@@ -165,6 +165,297 @@ import "../_libs/lie.mjs";
165
165
  import "../_libs/immediate.mjs";
166
166
  import "../_libs/setimmediate.mjs";
167
167
  import "../_libs/pako.mjs";
168
+ const WATCH_USAGE_PERCENT = 0.75;
169
+ const DANGER_USAGE_PERCENT = 0.9;
170
+ const LONG_TOOL_SCHEMA_TOKENS = 8e3;
171
+ const LARGE_ROLE_PERCENT = 0.35;
172
+ const DUPLICATE_MIN_TOKENS = 40;
173
+ const DUPLICATE_MIN_CHARS = 160;
174
+ const HISTORY_BLOAT_SEGMENTS = 18;
175
+ const HISTORY_BLOAT_TOKENS = 6e4;
176
+ const HISTORY_BLOAT_PERCENT = 0.7;
177
+ const SYSTEM_HEAVY_TOKENS = 3e3;
178
+ const SYSTEM_HEAVY_PERCENT = 0.3;
179
+ const EXPLICIT_CONTEXT_WINDOW_FIELDS = [
180
+ "context_window",
181
+ "contextWindow",
182
+ "context_length",
183
+ "contextLength",
184
+ "max_context_length",
185
+ "maxContextLength",
186
+ "max_input_tokens",
187
+ "maxInputTokens"
188
+ ];
189
+ const OUTPUT_RESERVE_FIELDS = [
190
+ "max_tokens",
191
+ "maxTokens",
192
+ "max_completion_tokens",
193
+ "maxCompletionTokens",
194
+ "max_output_tokens",
195
+ "maxOutputTokens"
196
+ ];
197
+ const MODEL_CONTEXT_RULES = [
198
+ {
199
+ family: "OpenAI GPT-4.1",
200
+ tokens: 1047576,
201
+ mode: "prefix",
202
+ patterns: ["gpt-4.1"]
203
+ },
204
+ {
205
+ family: "OpenAI GPT-4o",
206
+ tokens: 128e3,
207
+ mode: "prefix",
208
+ patterns: ["gpt-4o"]
209
+ },
210
+ {
211
+ family: "OpenAI o-series",
212
+ tokens: 128e3,
213
+ mode: "prefix",
214
+ patterns: ["o1", "o3", "o4"]
215
+ },
216
+ {
217
+ family: "Anthropic Claude",
218
+ tokens: 2e5,
219
+ mode: "prefix",
220
+ patterns: ["claude-"]
221
+ },
222
+ {
223
+ family: "DeepSeek",
224
+ tokens: 64e3,
225
+ mode: "prefix",
226
+ patterns: ["deepseek-"]
227
+ },
228
+ {
229
+ family: "MiniMax",
230
+ tokens: 1e6,
231
+ mode: "includes",
232
+ patterns: ["minimax"]
233
+ }
234
+ ];
235
+ const ROLE_LABELS = {
236
+ system: "System",
237
+ user: "User messages",
238
+ assistant: "Assistant messages",
239
+ tool: "Tool results",
240
+ tools: "Tool definitions"
241
+ };
242
+ function readPositiveInteger(value) {
243
+ if (typeof value === "number") {
244
+ if (Number.isFinite(value) && value > 0) return Math.floor(value);
245
+ return null;
246
+ }
247
+ if (typeof value === "string") {
248
+ const parsed = Number(value);
249
+ if (Number.isFinite(parsed) && parsed > 0) return Math.floor(parsed);
250
+ }
251
+ return null;
252
+ }
253
+ function readPositiveIntegerField(parsed, fields) {
254
+ for (const field of fields) {
255
+ const value = readPositiveInteger(safeGetOwnProperty(parsed, field));
256
+ if (value !== null) return value;
257
+ }
258
+ return null;
259
+ }
260
+ function readRequestModel(parsed) {
261
+ const value = safeGetOwnProperty(parsed, "model");
262
+ if (typeof value !== "string") return null;
263
+ const trimmed = value.trim();
264
+ return trimmed === "" ? null : trimmed;
265
+ }
266
+ function matchesRule(model, rule) {
267
+ switch (rule.mode) {
268
+ case "prefix":
269
+ return rule.patterns.some((pattern) => model.startsWith(pattern));
270
+ case "includes":
271
+ return rule.patterns.some((pattern) => model.includes(pattern));
272
+ }
273
+ }
274
+ function resolveContextWindow(model, parsed) {
275
+ const explicitWindow = readPositiveIntegerField(parsed, EXPLICIT_CONTEXT_WINDOW_FIELDS);
276
+ if (explicitWindow !== null) {
277
+ return {
278
+ tokens: explicitWindow,
279
+ label: "Request metadata",
280
+ source: "request"
281
+ };
282
+ }
283
+ if (model === null) {
284
+ return {
285
+ tokens: null,
286
+ label: "Unknown model",
287
+ source: "unknown"
288
+ };
289
+ }
290
+ const normalized = model.trim().toLowerCase();
291
+ for (const rule of MODEL_CONTEXT_RULES) {
292
+ if (matchesRule(normalized, rule)) {
293
+ return {
294
+ tokens: rule.tokens,
295
+ label: rule.family,
296
+ source: "model-rule"
297
+ };
298
+ }
299
+ }
300
+ return {
301
+ tokens: null,
302
+ label: "No local rule",
303
+ source: "unknown"
304
+ };
305
+ }
306
+ function clampPercent(value) {
307
+ if (value < 0) return 0;
308
+ if (value > 1) return 1;
309
+ return value;
310
+ }
311
+ function riskLevel(usagePercent) {
312
+ if (usagePercent === null) return "unknown";
313
+ if (usagePercent >= DANGER_USAGE_PERCENT) return "danger";
314
+ if (usagePercent >= WATCH_USAGE_PERCENT) return "watch";
315
+ return "ok";
316
+ }
317
+ function sumSegments(segments, role) {
318
+ return segments.filter((segment) => segment.role === role).reduce((sum, segment) => sum + segment.size, 0);
319
+ }
320
+ function buildRoleUsages(segments, total) {
321
+ const roles = ["system", "user", "assistant", "tool", "tools"];
322
+ return roles.map((role) => {
323
+ const tokens = sumSegments(segments, role);
324
+ return {
325
+ role,
326
+ label: ROLE_LABELS[role],
327
+ tokens,
328
+ percent: total > 0 ? tokens / total : 0
329
+ };
330
+ }).filter((usage) => usage.tokens > 0).sort((a, b) => b.tokens - a.tokens);
331
+ }
332
+ function normalizeDuplicateText(text) {
333
+ return text.replace(/\s+/g, " ").trim().toLowerCase();
334
+ }
335
+ function duplicateDiagnostic(segments) {
336
+ const buckets = /* @__PURE__ */ new Map();
337
+ for (const segment of segments) {
338
+ if (segment.size < DUPLICATE_MIN_TOKENS) continue;
339
+ if (segment.characters < DUPLICATE_MIN_CHARS) continue;
340
+ const normalized = normalizeDuplicateText(segment.text);
341
+ if (normalized === "") continue;
342
+ const existing = buckets.get(normalized);
343
+ if (existing === void 0) {
344
+ buckets.set(normalized, {
345
+ count: 1,
346
+ tokens: segment.size,
347
+ firstTokens: segment.size,
348
+ label: segment.label
349
+ });
350
+ continue;
351
+ }
352
+ buckets.set(normalized, {
353
+ count: existing.count + 1,
354
+ tokens: existing.tokens + segment.size,
355
+ firstTokens: existing.firstTokens,
356
+ label: existing.label
357
+ });
358
+ }
359
+ let repeatedGroups = 0;
360
+ let repeatedSegments = 0;
361
+ let repeatedTokens = 0;
362
+ let largest = null;
363
+ for (const bucket of buckets.values()) {
364
+ if (bucket.count < 2) continue;
365
+ repeatedGroups += 1;
366
+ repeatedSegments += bucket.count;
367
+ repeatedTokens += bucket.tokens - bucket.firstTokens;
368
+ if (largest === null || bucket.tokens > largest.tokens) largest = bucket;
369
+ }
370
+ if (repeatedGroups === 0 || largest === null) return null;
371
+ return {
372
+ kind: "duplicate",
373
+ severity: repeatedTokens >= LONG_TOOL_SCHEMA_TOKENS ? "danger" : "watch",
374
+ title: "Duplicate context",
375
+ detail: `${String(repeatedSegments)} repeated blocks across ${String(
376
+ repeatedGroups
377
+ )} group${repeatedGroups === 1 ? "" : "s"}, ~${String(repeatedTokens)} repeated tokens.`
378
+ };
379
+ }
380
+ function diagnosticsForRolePressure(roleUsages, total, messageSegmentCount) {
381
+ const diagnostics = [];
382
+ const toolUsage = roleUsages.find((usage) => usage.role === "tools") ?? null;
383
+ if (toolUsage !== null && (toolUsage.tokens >= LONG_TOOL_SCHEMA_TOKENS || toolUsage.percent >= LARGE_ROLE_PERCENT && toolUsage.tokens >= SYSTEM_HEAVY_TOKENS)) {
384
+ diagnostics.push({
385
+ kind: "tool-schema",
386
+ severity: toolUsage.tokens >= LONG_TOOL_SCHEMA_TOKENS ? "danger" : "watch",
387
+ title: "Large tool schema",
388
+ detail: `Tool definitions take ${Math.round(toolUsage.percent * 100)}% of request context.`
389
+ });
390
+ }
391
+ const systemUsage = roleUsages.find((usage) => usage.role === "system") ?? null;
392
+ if (systemUsage !== null && systemUsage.tokens >= SYSTEM_HEAVY_TOKENS && systemUsage.percent >= SYSTEM_HEAVY_PERCENT) {
393
+ diagnostics.push({
394
+ kind: "system-heavy",
395
+ severity: "watch",
396
+ title: "Large system prompt",
397
+ detail: `System instructions take ${Math.round(systemUsage.percent * 100)}% of request context.`
398
+ });
399
+ }
400
+ const historyTokens = roleUsages.filter((usage) => usage.role === "user" || usage.role === "assistant" || usage.role === "tool").reduce((sum, usage) => sum + usage.tokens, 0);
401
+ const historyPercent = total > 0 ? historyTokens / total : 0;
402
+ if (messageSegmentCount >= HISTORY_BLOAT_SEGMENTS && (historyTokens >= HISTORY_BLOAT_TOKENS || historyPercent >= HISTORY_BLOAT_PERCENT)) {
403
+ diagnostics.push({
404
+ kind: "history-bloat",
405
+ severity: historyPercent >= 0.85 ? "danger" : "watch",
406
+ title: "Conversation history growth",
407
+ detail: `${String(messageSegmentCount)} message blocks take ${Math.round(
408
+ historyPercent * 100
409
+ )}% of request context.`
410
+ });
411
+ }
412
+ return diagnostics;
413
+ }
414
+ function analyzeContextIntelligence({
415
+ segments,
416
+ inputTokens,
417
+ parsed,
418
+ model
419
+ }) {
420
+ const totalEstimatedTokens = segments.reduce((sum, segment) => sum + segment.size, 0);
421
+ const requestModel = model ?? readRequestModel(parsed);
422
+ const estimatedInputTokens = inputTokens ?? totalEstimatedTokens;
423
+ const outputReserveTokens = readPositiveIntegerField(parsed, OUTPUT_RESERVE_FIELDS);
424
+ const contextWindow = resolveContextWindow(requestModel, parsed);
425
+ const reservedTokens = outputReserveTokens ?? 0;
426
+ const windowUsedTokens = contextWindow.tokens === null ? null : estimatedInputTokens + reservedTokens;
427
+ const remainingInputTokens = contextWindow.tokens === null ? null : contextWindow.tokens - estimatedInputTokens;
428
+ const remainingAfterReserveTokens = contextWindow.tokens === null ? null : contextWindow.tokens - estimatedInputTokens - reservedTokens;
429
+ const usagePercent = contextWindow.tokens === null || windowUsedTokens === null ? null : clampPercent(windowUsedTokens / contextWindow.tokens);
430
+ const roleUsages = buildRoleUsages(segments, totalEstimatedTokens);
431
+ const largestRole = roleUsages[0] ?? null;
432
+ const messageSegmentCount = segments.filter((segment) => segment.role !== "tools").length;
433
+ const duplicate = duplicateDiagnostic(segments);
434
+ const diagnostics = diagnosticsForRolePressure(
435
+ roleUsages,
436
+ totalEstimatedTokens,
437
+ messageSegmentCount
438
+ );
439
+ if (duplicate !== null) diagnostics.unshift(duplicate);
440
+ return {
441
+ model: requestModel,
442
+ contextWindow,
443
+ estimatedInputTokens,
444
+ outputReserveTokens,
445
+ windowUsedTokens,
446
+ remainingInputTokens,
447
+ remainingAfterReserveTokens,
448
+ usagePercent,
449
+ riskLevel: riskLevel(usagePercent),
450
+ roleUsages,
451
+ largestRole,
452
+ diagnostics
453
+ };
454
+ }
455
+ const CONTEXT_USAGE_THRESHOLDS = {
456
+ watch: WATCH_USAGE_PERCENT,
457
+ danger: DANGER_USAGE_PERCENT
458
+ };
168
459
  const ANATOMY_ROLE_LABELS = {
169
460
  system: "System",
170
461
  user: "User",
@@ -383,12 +674,155 @@ const VIEW_MODE_OPTIONS = [
383
674
  { value: "role", label: "By Role" },
384
675
  { value: "segment", label: "By Segment" }
385
676
  ];
677
+ const RISK_CLASS = {
678
+ unknown: "bg-muted-foreground/50",
679
+ ok: "bg-emerald-500/70",
680
+ watch: "bg-amber-400/75",
681
+ danger: "bg-red-500/80"
682
+ };
683
+ const RISK_TEXT_CLASS = {
684
+ unknown: "text-muted-foreground",
685
+ ok: "text-emerald-400",
686
+ watch: "text-amber-300",
687
+ danger: "text-red-300"
688
+ };
386
689
  function formatPercent(value) {
387
690
  if (value >= 10) return `${value.toFixed(0)}%`;
388
691
  if (value >= 1) return `${value.toFixed(1)}%`;
389
692
  if (value > 0) return "<1%";
390
693
  return "0%";
391
694
  }
695
+ function formatUsagePercent(value) {
696
+ if (value === null) return "Unknown";
697
+ return formatPercent(value * 100);
698
+ }
699
+ function formatRemainingTokens(value) {
700
+ if (value === null) return "Unknown";
701
+ if (value < 0) return `-${formatTokens(Math.abs(value))}`;
702
+ return formatTokens(value);
703
+ }
704
+ function formatContextLimit(intelligence) {
705
+ if (intelligence.contextWindow.tokens === null) return "Unknown";
706
+ return formatTokens(intelligence.contextWindow.tokens);
707
+ }
708
+ function formatContextSource(intelligence) {
709
+ switch (intelligence.contextWindow.source) {
710
+ case "request":
711
+ return intelligence.contextWindow.label;
712
+ case "model-rule":
713
+ return intelligence.contextWindow.label;
714
+ case "unknown":
715
+ return intelligence.model === null ? intelligence.contextWindow.label : `${intelligence.contextWindow.label}: ${intelligence.model}`;
716
+ }
717
+ }
718
+ function diagnosticTone(diagnostic) {
719
+ switch (diagnostic.severity) {
720
+ case "watch":
721
+ return "border-amber-400/30 bg-amber-400/8 text-amber-100";
722
+ case "danger":
723
+ return "border-red-400/35 bg-red-500/10 text-red-100";
724
+ }
725
+ }
726
+ function ContextIntelligenceStrip({
727
+ intelligence
728
+ }) {
729
+ const usageWidth = intelligence.usagePercent === null ? 0 : Math.max(1, intelligence.usagePercent * 100);
730
+ const outputReserveLabel = intelligence.outputReserveTokens === null ? "No output reserve" : `Output reserve ${formatTokens(intelligence.outputReserveTokens)}`;
731
+ const largestRole = intelligence.largestRole;
732
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
733
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-x-4 gap-y-2 border-y border-border/60 py-2 text-xs sm:grid-cols-4", children: [
734
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
735
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] font-medium uppercase text-muted-foreground", children: "Window" }),
736
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-0.5 truncate font-mono text-foreground", children: formatContextLimit(intelligence) }),
737
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "truncate text-[11px] text-muted-foreground", children: formatContextSource(intelligence) })
738
+ ] }),
739
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
740
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] font-medium uppercase text-muted-foreground", children: "Used" }),
741
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("mt-0.5 font-mono", RISK_TEXT_CLASS[intelligence.riskLevel]), children: formatUsagePercent(intelligence.usagePercent) }),
742
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "truncate text-[11px] text-muted-foreground", children: outputReserveLabel })
743
+ ] }),
744
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
745
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] font-medium uppercase text-muted-foreground", children: "Remaining" }),
746
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-0.5 font-mono text-foreground", children: formatRemainingTokens(intelligence.remainingAfterReserveTokens) }),
747
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "truncate text-[11px] text-muted-foreground", children: [
748
+ "Input only ",
749
+ formatRemainingTokens(intelligence.remainingInputTokens)
750
+ ] })
751
+ ] }),
752
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
753
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] font-medium uppercase text-muted-foreground", children: "Largest" }),
754
+ largestRole === null ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-0.5 text-muted-foreground", children: "Unknown" }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
755
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-0.5 flex min-w-0 items-center gap-1.5", children: [
756
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
757
+ "span",
758
+ {
759
+ "aria-hidden": "true",
760
+ className: cn(
761
+ "size-2.5 shrink-0 rounded-[2px]",
762
+ ROLE_COLOR_CLASSES[largestRole.role]
763
+ )
764
+ }
765
+ ),
766
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate text-foreground", children: largestRole.label })
767
+ ] }),
768
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "truncate font-mono text-[11px] text-muted-foreground", children: [
769
+ formatPercent(largestRole.percent * 100),
770
+ " | ~",
771
+ formatTokens(largestRole.tokens)
772
+ ] })
773
+ ] })
774
+ ] })
775
+ ] }),
776
+ intelligence.contextWindow.tokens !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [
777
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative h-2 overflow-hidden rounded-full bg-muted/40", children: [
778
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
779
+ "div",
780
+ {
781
+ className: cn("h-full rounded-full", RISK_CLASS[intelligence.riskLevel]),
782
+ style: { width: `${String(usageWidth)}%` }
783
+ }
784
+ ),
785
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
786
+ "div",
787
+ {
788
+ "aria-hidden": "true",
789
+ className: "absolute top-0 h-full w-px bg-amber-200/70",
790
+ style: { left: `${String(CONTEXT_USAGE_THRESHOLDS.watch * 100)}%` }
791
+ }
792
+ ),
793
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
794
+ "div",
795
+ {
796
+ "aria-hidden": "true",
797
+ className: "absolute top-0 h-full w-px bg-red-200/80",
798
+ style: { left: `${String(CONTEXT_USAGE_THRESHOLDS.danger * 100)}%` }
799
+ }
800
+ )
801
+ ] }),
802
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-[10px] text-muted-foreground", children: [
803
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Context window" }),
804
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
805
+ "Watch ",
806
+ formatUsagePercent(CONTEXT_USAGE_THRESHOLDS.watch),
807
+ " / Danger",
808
+ " ",
809
+ formatUsagePercent(CONTEXT_USAGE_THRESHOLDS.danger)
810
+ ] })
811
+ ] })
812
+ ] }),
813
+ intelligence.diagnostics.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid gap-1.5 sm:grid-cols-2", children: intelligence.diagnostics.map((diagnostic) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
814
+ "div",
815
+ {
816
+ className: cn("rounded border px-2 py-1.5 text-xs", diagnosticTone(diagnostic)),
817
+ children: [
818
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-medium", children: diagnostic.title }),
819
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-0.5 text-[11px] text-muted-foreground", children: diagnostic.detail })
820
+ ]
821
+ },
822
+ `${diagnostic.kind}-${diagnostic.title}`
823
+ )) })
824
+ ] });
825
+ }
392
826
  function aggregateByRole(segments) {
393
827
  const result = [];
394
828
  for (const role of ROLE_ORDER) {
@@ -415,6 +849,7 @@ function topContributors(segments) {
415
849
  function RequestAnatomy({
416
850
  parsed,
417
851
  inputTokens,
852
+ model = null,
418
853
  onSegmentActivate,
419
854
  segments: providedSegments
420
855
  }) {
@@ -432,6 +867,15 @@ function RequestAnatomy({
432
867
  () => segments === null ? [] : topContributors(segments),
433
868
  [segments]
434
869
  );
870
+ const intelligence = reactExports.useMemo(
871
+ () => segments === null ? null : analyzeContextIntelligence({
872
+ segments,
873
+ inputTokens,
874
+ parsed,
875
+ model
876
+ }),
877
+ [inputTokens, model, parsed, segments]
878
+ );
435
879
  const showDivergenceWarning = reactExports.useMemo(() => {
436
880
  if (segments === null) return false;
437
881
  if (inputTokens === null) return false;
@@ -448,6 +892,8 @@ function RequestAnatomy({
448
892
  const activateSegment = viewMode === "segment" ? onSegmentActivate : void 0;
449
893
  const segmentCountLabel = `${String(segments.length)} segment${segments.length === 1 ? "" : "s"}`;
450
894
  const providerInputLabel = inputTokens === null ? "Provider input unknown" : `Provider input ${formatTokens(inputTokens)}`;
895
+ const contextLimitLabel = intelligence === null ? "Limit unknown" : `Limit ${formatContextLimit(intelligence)}`;
896
+ const usageLabel = intelligence === null ? "Usage unknown" : `${formatUsagePercent(intelligence.usagePercent)} used`;
451
897
  return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { delayDuration: 150, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3 space-y-3", "data-testid": "anatomy-root", children: [
452
898
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
453
899
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
@@ -458,6 +904,11 @@ function RequestAnatomy({
458
904
  " tokens | ",
459
905
  providerInputLabel,
460
906
  " | ",
907
+ contextLimitLabel,
908
+ " |",
909
+ " ",
910
+ usageLabel,
911
+ " | ",
461
912
  segmentCountLabel
462
913
  ] })
463
914
  ] }),
@@ -499,6 +950,7 @@ function RequestAnatomy({
499
950
  ] }),
500
951
  "Estimate differs from provider-reported input."
501
952
  ] }),
953
+ intelligence !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(ContextIntelligenceStrip, { intelligence }),
502
954
  /* @__PURE__ */ jsxRuntimeExports.jsx(
503
955
  SegmentBar,
504
956
  {
@@ -1,6 +1,6 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
2
- import { g as getLogFormatAdapter, f as formatTokens, c as cn, p as getStatusCategory, B as Badge, C as Collapsible, q as CollapsibleTrigger, s as CollapsibleContent, S as ScrollArea, t as JsonViewer, v as safeJsonValue } from "./ProxyViewerContainer-B5a9p3j5.mjs";
3
- import "./router-CVQaVF_3.mjs";
2
+ import { g as getLogFormatAdapter, f as formatTokens, c as cn, p as getStatusCategory, B as Badge, C as Collapsible, q as CollapsibleTrigger, s as CollapsibleContent, S as ScrollArea, t as JsonViewer, v as safeJsonValue } from "./ProxyViewerContainer-xesEFppM.mjs";
3
+ import "./router-BvuK0vQv.mjs";
4
4
  import "../_libs/modelcontextprotocol__server.mjs";
5
5
  import "../_libs/jszip.mjs";
6
6
  import { Z as Zap, j as TriangleAlert, ab as CircleStop, B as Brain, b as ChevronDown, f as ChevronRight, T as Terminal } from "../_libs/lucide-react.mjs";
@@ -1,6 +1,6 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
2
- import { k as TooltipProvider, l as Tooltip, m as TooltipTrigger, B as Badge, n as TooltipContent, t as JsonViewer } from "./ProxyViewerContainer-B5a9p3j5.mjs";
3
- import "./router-CVQaVF_3.mjs";
2
+ import { k as TooltipProvider, l as Tooltip, m as TooltipTrigger, B as Badge, n as TooltipContent, t as JsonViewer } from "./ProxyViewerContainer-xesEFppM.mjs";
3
+ import "./router-BvuK0vQv.mjs";
4
4
  import "../_libs/modelcontextprotocol__server.mjs";
5
5
  import "../_libs/jszip.mjs";
6
6
  import { b as ChevronDown, f as ChevronRight, L as LoaderCircle } from "../_libs/lucide-react.mjs";
@@ -1,6 +1,6 @@
1
- import { P as ProxyViewerContainer } from "./ProxyViewerContainer-B5a9p3j5.mjs";
1
+ import { P as ProxyViewerContainer } from "./ProxyViewerContainer-xesEFppM.mjs";
2
2
  import "../_libs/react.mjs";
3
- import "./router-CVQaVF_3.mjs";
3
+ import "./router-BvuK0vQv.mjs";
4
4
  import "../_libs/modelcontextprotocol__server.mjs";
5
5
  import "../_libs/jszip.mjs";
6
6
  import "../_libs/swr.mjs";
@@ -198,7 +198,7 @@ function getResponse() {
198
198
  return event.res;
199
199
  }
200
200
  async function getStartManifest(matchedRoutes) {
201
- const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-Bpe5ZC56.mjs");
201
+ const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-DGw3vVOm.mjs");
202
202
  const startManifest = tsrStartManifest();
203
203
  const rootRoute = startManifest.routes[rootRouteId] = startManifest.routes[rootRouteId] || {};
204
204
  rootRoute.assets = rootRoute.assets || [];
@@ -767,7 +767,7 @@ let entriesPromise;
767
767
  let baseManifestPromise;
768
768
  let cachedFinalManifestPromise;
769
769
  async function loadEntries() {
770
- const routerEntry = await import("./router-CVQaVF_3.mjs").then((n) => n.i);
770
+ const routerEntry = await import("./router-BvuK0vQv.mjs").then((n) => n.i);
771
771
  const startEntry = await import("./start-HYkvq4Ni.mjs");
772
772
  return { startEntry, routerEntry };
773
773
  }