@agimon-ai/doompi-log 0.0.1-alpha.67 → 0.0.1-alpha.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -8
- package/src/web/components/IssuesDetail.tsx +10 -10
- package/src/web/components/IssuesSection.tsx +5 -5
- package/src/web/components/MetricsNotice.tsx +4 -4
- package/src/web/components/MetricsPanel.tsx +4 -4
- package/src/web/components/MetricsReportView.tsx +10 -10
- package/src/web/components/charts/GroupBars.tsx +4 -4
- package/src/web/components/charts/TimelineChart.tsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/doompi-log",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.69",
|
|
4
4
|
"description": "Pi session metrics, findings, sink status, and a Log Metrics overlay for agent observability.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-observability",
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"src/web",
|
|
32
32
|
"src/exports/webClient.ts",
|
|
33
33
|
"src/types/webMetrics.ts",
|
|
34
|
-
"src/types/issueGrouping.ts"
|
|
34
|
+
"src/types/issueGrouping.ts",
|
|
35
|
+
"!src/web/**/*.stories.tsx"
|
|
35
36
|
],
|
|
36
37
|
"type": "module",
|
|
37
38
|
"main": "./dist/index.cjs",
|
|
@@ -78,11 +79,11 @@
|
|
|
78
79
|
"@agimon-ai/log-sink-mcp": "0.29.19",
|
|
79
80
|
"@deepseek-ai/cordis": "4.0.2",
|
|
80
81
|
"hono": "4.13.5",
|
|
81
|
-
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.
|
|
82
|
-
"@agimon-ai/doompi-
|
|
83
|
-
"@agimon-ai/doompi-
|
|
84
|
-
"@agimon-ai/doompi-web-
|
|
85
|
-
"@agimon-ai/doompi-web-components": "0.0.1-alpha.
|
|
82
|
+
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.68",
|
|
83
|
+
"@agimon-ai/doompi-web-contracts": "0.0.1-alpha.31",
|
|
84
|
+
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.67",
|
|
85
|
+
"@agimon-ai/doompi-web-security": "0.0.1-alpha.30",
|
|
86
|
+
"@agimon-ai/doompi-web-components": "0.0.1-alpha.28"
|
|
86
87
|
},
|
|
87
88
|
"devDependencies": {
|
|
88
89
|
"@earendil-works/pi-coding-agent": "0.85.1",
|
|
@@ -93,10 +94,11 @@
|
|
|
93
94
|
"@types/react": "19.2.18",
|
|
94
95
|
"@vitest/coverage-v8": "5.0.0",
|
|
95
96
|
"react": "19.2.8",
|
|
97
|
+
"react-dom": "19.2.8",
|
|
96
98
|
"tsdown": "0.23.0",
|
|
97
99
|
"typescript": "7.0.2",
|
|
98
100
|
"vitest": "5.0.0",
|
|
99
|
-
"@agimon-ai/doompi-ui": "0.0.1-alpha.
|
|
101
|
+
"@agimon-ai/doompi-ui": "0.0.1-alpha.69"
|
|
100
102
|
},
|
|
101
103
|
"peerDependencies": {
|
|
102
104
|
"@earendil-works/pi-coding-agent": "0.85.1",
|
|
@@ -40,13 +40,13 @@ function IssueRow({ group, max, tools }: IssueRowProps) {
|
|
|
40
40
|
return (
|
|
41
41
|
<li className="flex flex-col">
|
|
42
42
|
<div className="relative">
|
|
43
|
-
<span aria-hidden="true" className="absolute inset-y-0 left-0 rounded-
|
|
43
|
+
<span aria-hidden="true" className="absolute inset-y-0 left-0 rounded-xs bg-doom-red/20" style={{ width }} />
|
|
44
44
|
<button
|
|
45
45
|
type="button"
|
|
46
46
|
onClick={() => setOpen(!open)}
|
|
47
47
|
aria-expanded={open}
|
|
48
48
|
data-testid={`metrics-issue-${group.key}`}
|
|
49
|
-
className="relative flex w-full items-center gap-2 rounded-
|
|
49
|
+
className="relative flex w-full items-center gap-2 rounded-xs px-1 py-[3px] text-left text-xs hover:bg-doom-tint focus-visible:outline focus-visible:outline-1 focus-visible:outline-doom-blue"
|
|
50
50
|
>
|
|
51
51
|
<span className="w-8 shrink-0 text-right font-bold text-doom-red">{group.occurrences}</span>
|
|
52
52
|
<span className="w-24 shrink-0 truncate text-doom-hi">{titleOf(group)}</span>
|
|
@@ -56,8 +56,8 @@ function IssueRow({ group, max, tools }: IssueRowProps) {
|
|
|
56
56
|
</div>
|
|
57
57
|
|
|
58
58
|
{open ? (
|
|
59
|
-
<div className="flex flex-col gap-1 px-2 py-2 text-
|
|
60
|
-
<div className="flex flex-wrap gap-x-3 gap-y-1 text-
|
|
59
|
+
<div className="flex flex-col gap-1 px-2 py-2 text-xs" data-testid={`metrics-issue-body-${group.key}`}>
|
|
60
|
+
<div className="flex flex-wrap gap-x-3 gap-y-1 text-2xs text-doom-faint">
|
|
61
61
|
<span>category {group.category}</span>
|
|
62
62
|
{group.tool === null ? null : <span>tool {group.tool}</span>}
|
|
63
63
|
{group.errorType === null ? null : <span>error {group.errorType}</span>}
|
|
@@ -67,7 +67,7 @@ function IssueRow({ group, max, tools }: IssueRowProps) {
|
|
|
67
67
|
<span>last seen {group.lastSeen}</span>
|
|
68
68
|
</div>
|
|
69
69
|
<span className="break-words text-doom-dim">{group.detail}</span>
|
|
70
|
-
<ul className="flex flex-col gap-[1px] text-
|
|
70
|
+
<ul className="flex flex-col gap-[1px] text-2xs text-doom-faint">
|
|
71
71
|
{group.members.map((member, index) => (
|
|
72
72
|
<li key={`${member.timestamp}-${String(index)}`} className="flex flex-wrap gap-x-2">
|
|
73
73
|
<span>{member.timestamp}</span>
|
|
@@ -99,7 +99,7 @@ export function IssuesDetail({ view, tools }: IssuesDetailProps) {
|
|
|
99
99
|
|
|
100
100
|
return (
|
|
101
101
|
<div className="flex flex-col gap-3">
|
|
102
|
-
<span className="text-
|
|
102
|
+
<span className="text-xs text-doom-dim">
|
|
103
103
|
<span className="text-doom-hi">{view.totalIssues}</span> occurrences of{' '}
|
|
104
104
|
<span className="text-doom-hi">{groups.length}</span> distinct problems, worst first
|
|
105
105
|
</span>
|
|
@@ -113,8 +113,8 @@ export function IssuesDetail({ view, tools }: IssuesDetailProps) {
|
|
|
113
113
|
)}
|
|
114
114
|
|
|
115
115
|
<div className="flex flex-col gap-1">
|
|
116
|
-
<span className="text-
|
|
117
|
-
<table className="w-full text-
|
|
116
|
+
<span className="text-2xs font-bold text-doom-faint">failures by tool</span>
|
|
117
|
+
<table className="w-full text-xs" data-testid="metrics-issues-tools">
|
|
118
118
|
<tbody>
|
|
119
119
|
{countRows(view.byTool).map(([name, failures]) => {
|
|
120
120
|
const calls = callsByTool.get(name);
|
|
@@ -134,8 +134,8 @@ export function IssuesDetail({ view, tools }: IssuesDetailProps) {
|
|
|
134
134
|
</table>
|
|
135
135
|
</div>
|
|
136
136
|
|
|
137
|
-
<div className="flex flex-wrap gap-x-4 gap-y-1 text-
|
|
138
|
-
<span className="text-
|
|
137
|
+
<div className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-doom-dim">
|
|
138
|
+
<span className="text-2xs font-bold text-doom-faint">by category</span>
|
|
139
139
|
{countRows(view.byCategory).map(([name, count]) => (
|
|
140
140
|
<span key={name}>
|
|
141
141
|
{name} <span className="text-doom-hi">{count}</span>
|
|
@@ -50,14 +50,14 @@ export function IssuesSection({ tools, focus }: IssuesSectionProps) {
|
|
|
50
50
|
return (
|
|
51
51
|
<section className="flex flex-col gap-1" data-testid="metrics-issues">
|
|
52
52
|
<div className="flex items-center gap-2">
|
|
53
|
-
<span className="text-
|
|
53
|
+
<span className="text-2xs font-bold text-doom-faint">issues</span>
|
|
54
54
|
{open ? null : (
|
|
55
|
-
<Button variant="ghost" size="xs" className="text-
|
|
55
|
+
<Button variant="ghost" size="xs" className="text-2xs" onClick={load} data-testid="metrics-issues-open">
|
|
56
56
|
show detail
|
|
57
57
|
</Button>
|
|
58
58
|
)}
|
|
59
59
|
{open && !loading ? (
|
|
60
|
-
<Button variant="ghost" size="xs" className="text-
|
|
60
|
+
<Button variant="ghost" size="xs" className="text-2xs" onClick={load} data-testid="metrics-issues-reload">
|
|
61
61
|
reload
|
|
62
62
|
</Button>
|
|
63
63
|
) : null}
|
|
@@ -66,13 +66,13 @@ export function IssuesSection({ tools, focus }: IssuesSectionProps) {
|
|
|
66
66
|
{/* Reading this scans the whole window in a subprocess, so the reader is
|
|
67
67
|
told why it is behind a click rather than left wondering. */}
|
|
68
68
|
{open ? null : (
|
|
69
|
-
<span className="text-
|
|
69
|
+
<span className="text-2xs text-doom-faint/70">
|
|
70
70
|
the log sink has no issues endpoint, so this is read separately and takes a moment
|
|
71
71
|
</span>
|
|
72
72
|
)}
|
|
73
73
|
|
|
74
74
|
{message === '' ? null : (
|
|
75
|
-
<span className="text-
|
|
75
|
+
<span className="text-xs text-doom-yellow" data-testid="metrics-issues-message">
|
|
76
76
|
{message}
|
|
77
77
|
</span>
|
|
78
78
|
)}
|
|
@@ -39,19 +39,19 @@ export function FocusNotice({ requested, applied, dimension, onClear }: FocusNot
|
|
|
39
39
|
// whole numbers. Saying "showing model X" over them would be a lie, so the
|
|
40
40
|
// drill-down is reported as refused instead.
|
|
41
41
|
return (
|
|
42
|
-
<span className="text-
|
|
42
|
+
<span className="text-xs text-doom-yellow" data-testid="metrics-focus-refused">
|
|
43
43
|
this log sink does not support narrowing by {DIMENSION_LABELS[dimension]}, so the numbers below are still
|
|
44
44
|
everything
|
|
45
|
-
<Button variant="ghost" size="xs" className="ml-2 text-
|
|
45
|
+
<Button variant="ghost" size="xs" className="ml-2 text-2xs" onClick={onClear}>
|
|
46
46
|
clear
|
|
47
47
|
</Button>
|
|
48
48
|
</span>
|
|
49
49
|
);
|
|
50
50
|
}
|
|
51
51
|
return (
|
|
52
|
-
<span className="text-
|
|
52
|
+
<span className="text-xs text-doom-dim" data-testid="metrics-focus">
|
|
53
53
|
narrowed to <span className="text-doom-hi">{applied}</span>
|
|
54
|
-
<Button variant="ghost" size="xs" className="ml-2 text-
|
|
54
|
+
<Button variant="ghost" size="xs" className="ml-2 text-2xs" onClick={onClear}>
|
|
55
55
|
clear
|
|
56
56
|
</Button>
|
|
57
57
|
</span>
|
|
@@ -73,7 +73,7 @@ export function MetricsPanel(_props: SettingsPanelProps) {
|
|
|
73
73
|
setDimension(next as MetricsDimension);
|
|
74
74
|
}}
|
|
75
75
|
>
|
|
76
|
-
<SelectTrigger data-testid="metrics-dimension" className="w-[140px] text-
|
|
76
|
+
<SelectTrigger data-testid="metrics-dimension" className="w-[140px] text-xs">
|
|
77
77
|
<SelectValue />
|
|
78
78
|
</SelectTrigger>
|
|
79
79
|
<SelectContent>
|
|
@@ -85,7 +85,7 @@ export function MetricsPanel(_props: SettingsPanelProps) {
|
|
|
85
85
|
</SelectContent>
|
|
86
86
|
</Select>
|
|
87
87
|
<Select value={period} onValueChange={(next) => setPeriod(next as MetricsPeriod)}>
|
|
88
|
-
<SelectTrigger data-testid="metrics-period" className="w-[110px] text-
|
|
88
|
+
<SelectTrigger data-testid="metrics-period" className="w-[110px] text-xs">
|
|
89
89
|
<SelectValue />
|
|
90
90
|
</SelectTrigger>
|
|
91
91
|
<SelectContent>
|
|
@@ -100,7 +100,7 @@ export function MetricsPanel(_props: SettingsPanelProps) {
|
|
|
100
100
|
<Button
|
|
101
101
|
variant="ghost"
|
|
102
102
|
size="xs"
|
|
103
|
-
className="ml-auto text-
|
|
103
|
+
className="ml-auto text-2xs"
|
|
104
104
|
onClick={() => setPeriod((current) => current)}
|
|
105
105
|
disabled={loading}
|
|
106
106
|
>
|
|
@@ -109,7 +109,7 @@ export function MetricsPanel(_props: SettingsPanelProps) {
|
|
|
109
109
|
</div>
|
|
110
110
|
|
|
111
111
|
{error === '' ? null : (
|
|
112
|
-
<span className="text-
|
|
112
|
+
<span className="text-xs text-doom-red" data-testid="metrics-error">
|
|
113
113
|
{error}
|
|
114
114
|
</span>
|
|
115
115
|
)}
|
|
@@ -39,7 +39,7 @@ export function MetricsReportView({ report, onFocus }: MetricsReportViewProps) {
|
|
|
39
39
|
return (
|
|
40
40
|
<div className="flex flex-col gap-4">
|
|
41
41
|
<div className="flex flex-col gap-1">
|
|
42
|
-
<div className="flex flex-wrap items-center gap-3 text-
|
|
42
|
+
<div className="flex flex-wrap items-center gap-3 text-xs text-doom-dim">
|
|
43
43
|
<span>
|
|
44
44
|
<span className="text-doom-hi">{formatTokens(report.totals.totalTokens)}</span> total
|
|
45
45
|
</span>
|
|
@@ -70,41 +70,41 @@ export function MetricsReportView({ report, onFocus }: MetricsReportViewProps) {
|
|
|
70
70
|
beside it without this line reads as a breakdown that does not
|
|
71
71
|
add up, which is worse than not showing them.
|
|
72
72
|
*/}
|
|
73
|
-
<span className="text-
|
|
73
|
+
<span className="text-2xs text-doom-faint/70">
|
|
74
74
|
total is what the providers reported and is dominated by cache traffic; the parts beside it are counted
|
|
75
75
|
separately and do not sum to it
|
|
76
76
|
</span>
|
|
77
77
|
</div>
|
|
78
78
|
|
|
79
79
|
<section className="flex flex-col gap-1">
|
|
80
|
-
<span className="text-
|
|
80
|
+
<span className="text-2xs font-bold text-doom-faint">tokens over time</span>
|
|
81
81
|
<TimelineChart buckets={report.timeline} bucketUnit={report.bucketUnit} />
|
|
82
82
|
</section>
|
|
83
83
|
|
|
84
84
|
<section className="flex flex-col gap-1">
|
|
85
|
-
<span className="text-
|
|
85
|
+
<span className="text-2xs font-bold text-doom-faint">tokens by {DIMENSION_LABELS[report.dimension]}</span>
|
|
86
86
|
{DIMENSION_NOTES[report.dimension] === undefined ? null : (
|
|
87
|
-
<span className="text-
|
|
87
|
+
<span className="text-2xs text-doom-faint/70">{DIMENSION_NOTES[report.dimension]}</span>
|
|
88
88
|
)}
|
|
89
89
|
<GroupBars groups={report.groups} focus={report.focus} onFocus={onFocus} />
|
|
90
90
|
</section>
|
|
91
91
|
|
|
92
92
|
<section className="flex flex-col gap-1">
|
|
93
|
-
<span className="text-
|
|
93
|
+
<span className="text-2xs font-bold text-doom-faint">tool calls</span>
|
|
94
94
|
{/*
|
|
95
95
|
The token column is a ranking hint, not a measurement. The sink
|
|
96
96
|
attributes a turn's whole total to every tool that ran in that
|
|
97
97
|
turn, so saying otherwise here would be a lie the chart repeats.
|
|
98
98
|
*/}
|
|
99
|
-
<span className="text-
|
|
99
|
+
<span className="text-2xs text-doom-faint/70">
|
|
100
100
|
call counts are exact; the token column ranks tools by the turns they ran in, and is not each tool's own
|
|
101
101
|
consumption
|
|
102
102
|
</span>
|
|
103
|
-
<table className="w-full text-
|
|
103
|
+
<table className="w-full text-xs" data-testid="metrics-tools">
|
|
104
104
|
{/* Without heads the two right columns are just numbers; "1006" and
|
|
105
105
|
"325.9k" do not say which is a call count and which is tokens. */}
|
|
106
106
|
<thead>
|
|
107
|
-
<tr className="text-
|
|
107
|
+
<tr className="text-2xs text-doom-faint/70">
|
|
108
108
|
<th className="py-1 text-left font-normal">tool</th>
|
|
109
109
|
<th className="w-20 py-1 text-right font-normal">calls</th>
|
|
110
110
|
<th className="w-20 py-1 text-right font-normal">tokens</th>
|
|
@@ -124,7 +124,7 @@ export function MetricsReportView({ report, onFocus }: MetricsReportViewProps) {
|
|
|
124
124
|
|
|
125
125
|
<IssuesSection tools={report.tools} focus={report.dimension === 'session' ? report.focus : undefined} />
|
|
126
126
|
|
|
127
|
-
<span className="text-
|
|
127
|
+
<span className="text-2xs text-doom-faint/70" data-testid="metrics-provenance">
|
|
128
128
|
read over {report.transport ?? 'an unreported transport'} · generated {report.generatedAt}
|
|
129
129
|
</span>
|
|
130
130
|
</div>
|
|
@@ -28,7 +28,7 @@ export function GroupBars({ groups, focus, onFocus }: GroupBarsProps) {
|
|
|
28
28
|
{/* The two right-hand columns were bare numbers. A row reading "11.0M 12"
|
|
29
29
|
gave no way to know the second figure counted issues. Same widths and
|
|
30
30
|
padding as a row, so the heads sit over their own columns. */}
|
|
31
|
-
<div aria-hidden className="flex items-center gap-2 px-1 text-
|
|
31
|
+
<div aria-hidden className="flex items-center gap-2 px-1 text-2xs text-doom-faint/70">
|
|
32
32
|
<span className="min-w-0 flex-1" />
|
|
33
33
|
<span className="w-14 shrink-0 text-right">tokens</span>
|
|
34
34
|
<span className="w-10 shrink-0 text-right">issues</span>
|
|
@@ -58,18 +58,18 @@ export function GroupBars({ groups, focus, onFocus }: GroupBarsProps) {
|
|
|
58
58
|
track for horizontal space. */}
|
|
59
59
|
<span
|
|
60
60
|
aria-hidden="true"
|
|
61
|
-
className={`absolute inset-y-0 left-0 rounded-
|
|
61
|
+
className={`absolute inset-y-0 left-0 rounded-xs ${selected ? 'bg-doom-blue/30' : 'bg-doom-blue/15'}`}
|
|
62
62
|
style={{ width }}
|
|
63
63
|
/>
|
|
64
64
|
{onFocus === undefined ? (
|
|
65
|
-
<span className="relative flex items-center gap-2 px-1 py-[3px] text-
|
|
65
|
+
<span className="relative flex items-center gap-2 px-1 py-[3px] text-xs">{row}</span>
|
|
66
66
|
) : (
|
|
67
67
|
<button
|
|
68
68
|
type="button"
|
|
69
69
|
onClick={() => onFocus(selected ? '' : group.key)}
|
|
70
70
|
aria-pressed={selected}
|
|
71
71
|
data-testid={`metrics-group-${group.key}`}
|
|
72
|
-
className="relative flex w-full items-center gap-2 rounded-
|
|
72
|
+
className="relative flex w-full items-center gap-2 rounded-xs px-1 py-[3px] text-xs hover:bg-doom-tint focus-visible:outline focus-visible:outline-1 focus-visible:outline-doom-blue"
|
|
73
73
|
>
|
|
74
74
|
{row}
|
|
75
75
|
</button>
|
|
@@ -62,7 +62,7 @@ export function TimelineChart({ buckets, bucketUnit }: TimelineChartProps) {
|
|
|
62
62
|
);
|
|
63
63
|
})}
|
|
64
64
|
</svg>
|
|
65
|
-
<div className="flex flex-wrap justify-between gap-x-3 text-
|
|
65
|
+
<div className="flex flex-wrap justify-between gap-x-3 text-2xs text-doom-faint">
|
|
66
66
|
<span>{buckets[0]?.label ?? ''}</span>
|
|
67
67
|
{buckets.length > 1 ? <span>peak {formatTokens(max)}</span> : null}
|
|
68
68
|
<span>
|