Skip to content

Commit e7aa95f

Browse files
feat: unify usage metrics and add Kimi Work support (#1234)
Unifies cost and token analytics in one Usage page with a URL-backed metric toggle, metric-aware visual formatting, and server-side rankings across SQLite, PostgreSQL, and DuckDB. Legacy token-usage links remain compatible. Adds Kimi Work desktop discovery and parsing with documented format provenance, plus timestamp-aware Kimi alias pricing. Exact custom alias rates retain precedence, while schema-v4 pricing metadata preserves reported model names and every resolved priced model across cutoff-spanning reports. Adds macOS production and development launchers that use private temporary log directories and open only process-authenticated, dynamically discovered loopback URLs. K3 rates remain curated estimates until they are available from the upstream pricing catalog; consumers of the changed pricing provenance shape must use schema version 4. Co-authored-by: 潦草学者 <liaocaoxuezhe@users.noreply.github.com>
1 parent 5c0f0e7 commit e7aa95f

108 files changed

Lines changed: 5966 additions & 627 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ thread JSON files.
339339
| Kilo | `~/.local/share/kilo/` |
340340
| Kilo (legacy) | `~/Library/Application Support/Code/User/globalStorage/kilocode.kilo-code/` (macOS), `~/.config/Code/User/globalStorage/kilocode.kilo-code/` (Linux) |
341341
| Kimi | `~/.kimi/sessions/` |
342+
| Kimi Work | `~/Library/Application Support/kimi-desktop/daimon-share/daimon/runtime/kimi-code/home/sessions/` (macOS) |
342343
| Kiro CLI | `~/.kiro/sessions/cli/`, `~/.local/share/kiro-cli/` |
343344
| Kiro IDE | `~/Library/Application Support/Kiro/` (macOS) |
344345
| MiMoCode | `~/.local/share/mimocode/` |

cmd/agentsview/activity_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,5 +342,5 @@ func TestActivityReportGolden(t *testing.T) {
342342
})
343343
require.NoError(t, err, "activity report json golden command")
344344

345-
assertGoldenBytes(t, "activity_report_v3.json", []byte(stdout))
345+
assertGoldenBytes(t, "activity_report_v4.json", []byte(stdout))
346346
}

cmd/agentsview/export.go

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -476,15 +476,16 @@ func mergeExportSessionsPricing(
476476

477477
merged := cloneExportSessionsPricing(base)
478478
if merged.Models == nil {
479-
merged.Models = map[string]export.EffectiveModelRate{}
479+
merged.Models = map[string]export.ModelPricingProvenance{}
480480
}
481-
for model, rate := range next.Models {
481+
for model, provenance := range next.Models {
482482
if existing, ok := merged.Models[model]; ok {
483-
rate = mergeExportSessionsModelRate(existing, rate)
483+
provenance = mergeExportSessionsModelProvenance(
484+
existing, provenance)
484485
} else {
485-
rate = cloneExportSessionsModelRate(rate)
486+
provenance = cloneExportSessionsModelProvenance(provenance)
486487
}
487-
merged.Models[model] = rate
488+
merged.Models[model] = provenance
488489
}
489490
merged.Fallback.Models = mergeExportSessionsStringSets(
490491
merged.Fallback.Models, next.Fallback.Models)
@@ -524,14 +525,27 @@ func cloneExportSessionsPricing(
524525
clone := *block
525526
clone.Fallback.Models = append([]string{}, block.Fallback.Models...)
526527
clone.Fallback.Used = len(clone.Fallback.Models) > 0
527-
clone.Models = make(map[string]export.EffectiveModelRate,
528+
clone.Models = make(map[string]export.ModelPricingProvenance,
528529
len(block.Models))
529-
for model, rate := range block.Models {
530-
clone.Models[model] = cloneExportSessionsModelRate(rate)
530+
for model, provenance := range block.Models {
531+
clone.Models[model] =
532+
cloneExportSessionsModelProvenance(provenance)
531533
}
532534
return &clone
533535
}
534536

537+
func cloneExportSessionsModelProvenance(
538+
provenance export.ModelPricingProvenance,
539+
) export.ModelPricingProvenance {
540+
clone := provenance
541+
clone.Resolutions = make([]export.EffectiveModelRate,
542+
len(provenance.Resolutions))
543+
for i, rate := range provenance.Resolutions {
544+
clone.Resolutions[i] = cloneExportSessionsModelRate(rate)
545+
}
546+
return clone
547+
}
548+
535549
func cloneExportSessionsModelRate(
536550
rate export.EffectiveModelRate,
537551
) export.EffectiveModelRate {
@@ -542,19 +556,63 @@ func cloneExportSessionsModelRate(
542556
return rate
543557
}
544558

545-
func mergeExportSessionsModelRate(
546-
base, next export.EffectiveModelRate,
547-
) export.EffectiveModelRate {
548-
merged := cloneExportSessionsModelRate(base)
549-
if merged.MatchedPattern == nil && next.MatchedPattern != nil {
550-
pattern := *next.MatchedPattern
551-
merged.MatchedPattern = &pattern
552-
}
559+
type exportSessionsModelResolutionKey struct {
560+
pricedModel string
561+
matchedPattern string
562+
hasMatchedPattern bool
563+
}
564+
565+
func mergeExportSessionsModelProvenance(
566+
base, next export.ModelPricingProvenance,
567+
) export.ModelPricingProvenance {
568+
merged := cloneExportSessionsModelProvenance(base)
553569
merged.CostSource = mergeExportSessionsCostSource(
554570
merged.CostSource, next.CostSource)
571+
positions := make(map[exportSessionsModelResolutionKey]int,
572+
len(merged.Resolutions))
573+
for i, rate := range merged.Resolutions {
574+
positions[exportSessionsModelRateKey(rate)] = i
575+
}
576+
for _, rate := range next.Resolutions {
577+
key := exportSessionsModelRateKey(rate)
578+
if i, ok := positions[key]; ok {
579+
merged.Resolutions[i].CostSource =
580+
mergeExportSessionsCostSource(
581+
merged.Resolutions[i].CostSource,
582+
rate.CostSource)
583+
continue
584+
}
585+
positions[key] = len(merged.Resolutions)
586+
merged.Resolutions = append(merged.Resolutions,
587+
cloneExportSessionsModelRate(rate))
588+
}
589+
sort.Slice(merged.Resolutions, func(i, j int) bool {
590+
left := exportSessionsModelRateKey(merged.Resolutions[i])
591+
right := exportSessionsModelRateKey(merged.Resolutions[j])
592+
if left.pricedModel != right.pricedModel {
593+
return left.pricedModel < right.pricedModel
594+
}
595+
if left.hasMatchedPattern != right.hasMatchedPattern {
596+
return !left.hasMatchedPattern
597+
}
598+
return left.matchedPattern < right.matchedPattern
599+
})
555600
return merged
556601
}
557602

603+
func exportSessionsModelRateKey(
604+
rate export.EffectiveModelRate,
605+
) exportSessionsModelResolutionKey {
606+
key := exportSessionsModelResolutionKey{
607+
pricedModel: rate.PricedModel,
608+
}
609+
if rate.MatchedPattern != nil {
610+
key.matchedPattern = *rate.MatchedPattern
611+
key.hasMatchedPattern = true
612+
}
613+
return key
614+
}
615+
558616
func mergeExportSessionsCostSource(
559617
a, b export.CostSource,
560618
) export.CostSource {

cmd/agentsview/export_sessions_test.go

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,15 @@ func TestMergeExportSessionsPricingTreatsOnlyComputedNoModelPagesAsNeutral(
249249
) {
250250
noModels := &export.PricingBlock{
251251
CostSource: export.CostSourceComputed,
252-
Models: map[string]export.EffectiveModelRate{},
252+
Models: map[string]export.ModelPricingProvenance{},
253253
}
254254
mixedNoModels := &export.PricingBlock{
255255
CostSource: export.CostSourceMixed,
256-
Models: map[string]export.EffectiveModelRate{},
256+
Models: map[string]export.ModelPricingProvenance{},
257257
}
258258
reported := &export.PricingBlock{
259259
CostSource: export.CostSourceReported,
260-
Models: map[string]export.EffectiveModelRate{
260+
Models: map[string]export.ModelPricingProvenance{
261261
"reported-model": {
262262
CostSource: export.CostSourceReported,
263263
},
@@ -280,6 +280,53 @@ func TestMergeExportSessionsPricingTreatsOnlyComputedNoModelPagesAsNeutral(
280280
assert.Equal(t, export.CostSourceMixed, got.CostSource)
281281
}
282282

283+
func TestMergeExportSessionsPricingCombinesReportedModelResolutions(t *testing.T) {
284+
base := &export.PricingBlock{
285+
CostSource: export.CostSourceComputed,
286+
Models: map[string]export.ModelPricingProvenance{
287+
"kimi-for-coding": {
288+
CostSource: export.CostSourceComputed,
289+
Resolutions: []export.EffectiveModelRate{{
290+
PricedModel: "kimi-k3",
291+
CostSource: export.CostSourceComputed,
292+
}},
293+
},
294+
},
295+
}
296+
next := &export.PricingBlock{
297+
CostSource: export.CostSourceMixed,
298+
Models: map[string]export.ModelPricingProvenance{
299+
"kimi-for-coding": {
300+
CostSource: export.CostSourceMixed,
301+
Resolutions: []export.EffectiveModelRate{
302+
{
303+
PricedModel: "moonshot/kimi-k2.6",
304+
CostSource: export.CostSourceComputed,
305+
},
306+
{
307+
PricedModel: "kimi-k3",
308+
CostSource: export.CostSourceReported,
309+
},
310+
},
311+
},
312+
},
313+
}
314+
315+
got := mergeExportSessionsPricing(base, next)
316+
317+
require.Contains(t, got.Models, "kimi-for-coding")
318+
provenance := got.Models["kimi-for-coding"]
319+
assert.Equal(t, export.CostSourceMixed, provenance.CostSource)
320+
require.Len(t, provenance.Resolutions, 2)
321+
assert.Equal(t, "kimi-k3", provenance.Resolutions[0].PricedModel)
322+
assert.Equal(t, export.CostSourceMixed,
323+
provenance.Resolutions[0].CostSource)
324+
assert.Equal(t, "moonshot/kimi-k2.6",
325+
provenance.Resolutions[1].PricedModel)
326+
assert.Equal(t, export.CostSourceComputed,
327+
provenance.Resolutions[1].CostSource)
328+
}
329+
283330
func TestExportSessionsAllNDJSONCursorNextEmpty(t *testing.T) {
284331
seedExportSessionsArchive(t)
285332

@@ -667,7 +714,7 @@ func TestExportSessionsJSONGolden(t *testing.T) {
667714
assert.NotContains(t, stdout, `"machine":"golden-host"`)
668715
assert.NotContains(t, stdout, `"root_path":"/`)
669716

670-
assertGoldenBytes(t, "session_export_v3.json", []byte(stdout))
717+
assertGoldenBytes(t, "session_export_v4.json", []byte(stdout))
671718
}
672719

673720
func TestExportSessionsNDJSONGolden(t *testing.T) {
@@ -682,7 +729,7 @@ func TestExportSessionsNDJSONGolden(t *testing.T) {
682729
require.NoError(t, err, "export sessions ndjson golden")
683730
require.Empty(t, stderr)
684731

685-
assertGoldenBytes(t, "session_export_v3.ndjson", []byte(stdout))
732+
assertGoldenBytes(t, "session_export_v4.ndjson", []byte(stdout))
686733
}
687734

688735
func firstExportSessionsCursor(t *testing.T) string {

0 commit comments

Comments
 (0)