noctrace 0.8.1 → 0.9.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.
@@ -7,7 +7,7 @@
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com" />
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
9
  <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&display=swap" rel="stylesheet" />
10
- <script type="module" crossorigin src="/assets/index-B37clQwh.js"></script>
10
+ <script type="module" crossorigin src="/assets/index-D3XepZ5e.js"></script>
11
11
  <link rel="stylesheet" crossorigin href="/assets/index-DwPuae45.css">
12
12
  </head>
13
13
  <body>
@@ -67,6 +67,11 @@ export function parseFilterString(filter) {
67
67
  result.typeFilters.push('agent');
68
68
  continue;
69
69
  }
70
+ // 'turn' keyword: treated as type filter
71
+ if (lower === 'turn') {
72
+ result.typeFilters.push('turn');
73
+ continue;
74
+ }
70
75
  // Everything else is free text
71
76
  result.textTokens.push(lower);
72
77
  }
@@ -636,6 +636,106 @@ export function parseJsonlContent(content) {
636
636
  });
637
637
  }
638
638
  }
639
+ // Create turn rows for user prompts (string content = human text, not tool results)
640
+ for (const rec of records) {
641
+ if (rec.type !== 'user')
642
+ continue;
643
+ const ur = rec;
644
+ // Array content means tool_result records — skip
645
+ if (Array.isArray(ur.message.content))
646
+ continue;
647
+ if (ur.isMeta === true)
648
+ continue;
649
+ const raw = rec;
650
+ if (raw['isSynthetic'] === true)
651
+ continue;
652
+ const text = typeof ur.message.content === 'string' ? ur.message.content : '';
653
+ if (!text.trim())
654
+ continue;
655
+ const ts = new Date(ur.timestamp).getTime();
656
+ const truncated = text.length > 120 ? text.slice(0, 117) + '...' : text;
657
+ top.push({
658
+ id: `turn-user-${ur.uuid}`,
659
+ type: 'turn',
660
+ toolName: 'UserPrompt',
661
+ label: truncated,
662
+ startTime: ts,
663
+ endTime: ts,
664
+ duration: 0,
665
+ status: 'success',
666
+ parentAgentId: null,
667
+ input: {},
668
+ output: text,
669
+ inputTokens: 0,
670
+ outputTokens: 0,
671
+ tokenDelta: 0,
672
+ contextFillPercent: 0,
673
+ isReread: false,
674
+ isFailure: false,
675
+ children: [],
676
+ tips: [],
677
+ modelName: null,
678
+ estimatedCost: null,
679
+ agentType: null,
680
+ agentColor: null,
681
+ sequence: null,
682
+ isFastMode: false,
683
+ parentToolUseId: null,
684
+ });
685
+ }
686
+ // Create turn rows for assistant text-only responses (no tool_use blocks)
687
+ for (const rec of records) {
688
+ if (rec.type !== 'assistant')
689
+ continue;
690
+ const ar = rec;
691
+ const hasToolUse = ar.message.content.some(b => isToolUse(b));
692
+ if (hasToolUse)
693
+ continue; // already handled by the tool row creation loop
694
+ if (ar.message.error)
695
+ continue; // already handled by api-error loop
696
+ const texts = ar.message.content
697
+ .filter((b) => b.type === 'text')
698
+ .map(b => b.text);
699
+ const fullText = texts.join('\n');
700
+ if (!fullText.trim())
701
+ continue;
702
+ const ts = new Date(ar.timestamp).getTime();
703
+ const truncated = fullText.length > 120 ? fullText.slice(0, 117) + '...' : fullText;
704
+ const usage = ar.message.usage;
705
+ const inputTokens = (usage?.input_tokens ?? 0)
706
+ + (usage?.cache_creation_input_tokens ?? 0)
707
+ + (usage?.cache_read_input_tokens ?? 0);
708
+ const outputTokens = usage?.output_tokens ?? 0;
709
+ const modelName = typeof ar.message.model === 'string' ? ar.message.model : null;
710
+ top.push({
711
+ id: `turn-asst-${ar.uuid}`,
712
+ type: 'turn',
713
+ toolName: 'AssistantResponse',
714
+ label: truncated,
715
+ startTime: ts,
716
+ endTime: ts,
717
+ duration: 0,
718
+ status: 'success',
719
+ parentAgentId: null,
720
+ input: {},
721
+ output: fullText,
722
+ inputTokens,
723
+ outputTokens,
724
+ tokenDelta: 0,
725
+ contextFillPercent: (inputTokens / effectiveWindow) * 100,
726
+ isReread: false,
727
+ isFailure: false,
728
+ children: [],
729
+ tips: [],
730
+ modelName,
731
+ estimatedCost: null,
732
+ agentType: null,
733
+ agentColor: null,
734
+ sequence: typeof ar.sequence === 'number' ? ar.sequence : null,
735
+ isFastMode: ar.message.speed === 'fast',
736
+ parentToolUseId: null,
737
+ });
738
+ }
639
739
  return top;
640
740
  }
641
741
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "noctrace",
3
- "version": "0.8.1",
4
- "description": "Chrome DevTools Network-tab-style waterfall visualizer for Claude Code agent workflows",
3
+ "version": "0.9.0",
4
+ "description": "Claude Code observability — DevTools-style waterfall visualizer for AI agent workflows, token tracking, and context health monitoring",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Nyktora Group LLC",
@@ -19,16 +19,20 @@
19
19
  "keywords": [
20
20
  "noctrace",
21
21
  "claude-code",
22
+ "claude-code-observability",
22
23
  "claude",
23
24
  "anthropic",
24
25
  "observability",
25
26
  "devtools",
26
27
  "waterfall",
27
28
  "ai-agent",
29
+ "ai-agent-observability",
28
30
  "llm",
31
+ "llm-observability",
29
32
  "timeline",
30
33
  "visualizer",
31
34
  "claude-code-hooks",
35
+ "claude-code-monitoring",
32
36
  "token-usage",
33
37
  "context-window",
34
38
  "agent-monitoring",
@@ -36,7 +40,9 @@
36
40
  "claude-code-devtools",
37
41
  "session-viewer",
38
42
  "tool-calls",
39
- "sub-agents"
43
+ "sub-agents",
44
+ "opentelemetry",
45
+ "context-health"
40
46
  ],
41
47
  "bin": {
42
48
  "noctrace": "bin/noctrace.js"