Files
dsh-context/tests/client/overview.rows.spec.ts
bowenliang123 026ffca599 feat(context): fold the range off the step-start daily ledger
Context Insights now reads contextActivity for every range-scoped figure: a step's settlement, tool run, and wall time book on the local day its step/start fired, so a step crossing midnight stays on its start day. The KPI band, Token Stats, and session selection share the daily ledger; turns, tool calls, and step wall time join each day, and contextActivity's stateVersion moves to 5 so cached rows cold-refold. The Activity heatmap drops the Steps metric for daily token volume; Timing Stats stays session-lifetime and is labeled as such.

Refs #172
2026-10-20 08:42:51 +08:00

347 lines
15 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
import { billedOf, coveredRowsOf, rowsOfSnapshot, sessionsSnapshotOf, tokenPartsOf } from '../../src/client/overview'
import { mergeCostUsage } from '../../src/client/cost'
import type { SessionCostUsage } from '../../src/shared/types'
import { COST } from './overview.fixtures'
/** The minimal wire-valid timeline head, overridable per case. */
function timelineOf(over: Record<string, unknown> = {}): Record<string, unknown> {
return {
ok: true,
current: { system: 10, tools: 20, user: 30, inject: 5, skill: 5, assistant: 40, tool: 50, total: 160 },
requests: [],
events: [],
nodes: [],
droppedNodes: 0,
archive: [],
...over,
}
}
/** A session-list snapshot over rows of `{ id, row }` pairs. */
function snapshotOf(entries: [string, Record<string, unknown>][], current?: string): Record<string, unknown> {
return {
ids: entries.map(([id]) => id),
byId: Object.fromEntries(entries),
current,
phase: 'ready',
}
}
describe('sessionsSnapshotOf', () => {
test('a non-function seat reads null', () => {
assert.equal(sessionsSnapshotOf({}), null)
assert.equal(sessionsSnapshotOf({ useSessions: 7 }), null)
})
test('a throwing seat reads null instead of taking the render down', () => {
assert.equal(sessionsSnapshotOf({ useSessions: () => { throw new Error('boom') } }), null)
})
test('the raw snapshot passes through', () => {
const snap = { ids: [] }
assert.equal(sessionsSnapshotOf({ useSessions: (sel: (s: unknown) => unknown) => sel(snap) }), snap)
})
})
describe('rowsOfSnapshot', () => {
test('unusable snapshots read null (the unavailable note, not an empty list)', () => {
assert.equal(rowsOfSnapshot(null), null)
assert.equal(rowsOfSnapshot(7), null)
assert.equal(rowsOfSnapshot({}), null, 'ids missing')
assert.equal(rowsOfSnapshot({ ids: 'x' }), null, 'ids not an array')
})
test('an empty ready list reads as a real empty array', () => {
assert.deepEqual(rowsOfSnapshot({ ids: [], byId: {} }), [])
assert.deepEqual(rowsOfSnapshot({ ids: ['s1'] }), [], 'byId absent: the row is skipped')
assert.deepEqual(rowsOfSnapshot({ ids: [7, 's1'], byId: {} }), [], 'non-string ids drop')
})
test('blank rows drop; metadata derives on the display ladder', () => {
const rows = rowsOfSnapshot(snapshotOf([
['a', { blank: true, displayTitle: 'blank one' }],
['b', { displayTitle: 'shown', updatedAt: 5, running: true }],
['c', { title: 'titled' }],
['d', {}],
['e', { displayTitle: '', title: '', cwd: '/repo', updatedAt: 'x' }],
], 'b'))
assert.ok(rows !== null)
assert.equal(rows.length, 4)
const [b, c, d, e] = rows
assert.deepEqual(b, {
id: 'b', title: 'shown', updatedAt: 5, running: true, current: true, archived: false,
timeline: null, activity: null, family: [null], familyActivity: [null], familyCost: null,
})
assert.equal(c.title, 'titled')
assert.equal(c.current, false)
assert.equal(d.title, 'd', 'the id is the last-resort title')
assert.equal(e.title, 'e', 'empty strings fall through the ladder')
assert.equal(e.cwd, '/repo')
assert.equal(e.updatedAt, 0, 'a non-numeric stamp zeroes')
})
test('archived sessions are tagged, not dropped; a hostile archive set fails open', () => {
const snap = snapshotOf([
['a', { displayTitle: 'live' }],
['b', { displayTitle: 'archived' }],
['c', { displayTitle: 'kept' }],
])
const rows = rowsOfSnapshot(snap, { archivedSessionIds: ['b', 7, null] })
assert.ok(rows !== null)
assert.deepEqual(rows.map(r => r.id), ['a', 'b', 'c'])
assert.equal(rows[1].archived, true, 'the archived id tags its row')
assert.equal(rows[0].archived, false)
assert.equal(rows[2].archived, false, 'non-string entries archive nothing')
// Fail-open shapes: an absent seat, a bare record, a malformed set, and a throwing accessor all keep the full list.
assert.equal(rowsOfSnapshot(snap)?.length, 3)
assert.equal(rowsOfSnapshot(snap, null)?.length, 3)
assert.equal(rowsOfSnapshot(snap, 7)?.length, 3)
assert.equal(rowsOfSnapshot(snap, {})?.length, 3)
assert.equal(rowsOfSnapshot(snap, { archivedSessionIds: 'x' })?.length, 3)
assert.equal(rowsOfSnapshot(snap, { get archivedSessionIds() { throw new Error('boom') } })?.length, 3)
})
test('projection values are sanitized per row; hostile payloads degrade to nulls', () => {
const rows = rowsOfSnapshot(snapshotOf([
['a', {
displayTitle: 'good',
projectionValues: {
contextTimeline: timelineOf({ counts: { turns: 2, steps: 3, injects: 0, compactions: 0, prunes: 0 } }),
contextActivity: { days: { '2026-09-16': { tokens: 15, requests: 1 } } },
},
}],
['b', { displayTitle: 'junk', projectionValues: { contextTimeline: 7, contextActivity: 'x' } }],
]))
assert.ok(rows !== null)
const [a, b] = rows
assert.equal(a.timeline?.counts?.turns, 2)
assert.equal(a.activity?.days['2026-09-16'].tokens, 15)
assert.equal(b.timeline, null)
assert.equal(b.activity, null)
})
test('a hostile row that throws on access drops whole; the list survives', () => {
const hostile = new Proxy({}, { get: () => { throw new Error('boom') } })
const rows = rowsOfSnapshot({ ids: ['x', 'y'], byId: { x: hostile, y: { displayTitle: 'fine' } } })
assert.ok(rows !== null)
assert.equal(rows.length, 1)
assert.equal(rows[0].id, 'y')
})
})
describe('rowsOfSnapshot: subagent rows', () => {
test('subagent-origin rows drop out (the workspace browser’s own exclusion)', () => {
const rows = rowsOfSnapshot(snapshotOf([
['main', { displayTitle: 'main session' }],
['child', { displayTitle: 'subagent run', origin: 'subagent', parentId: 'main' }],
]))
assert.ok(rows !== null)
assert.deepEqual(rows.map(r => r.id), ['main'])
})
})
describe('rowsOfSnapshot: the family fold', () => {
const COST2: SessionCostUsage = { zhipuai: { m: { peak: { uncached: 300, cacheRead: 0, cacheWrite: 0, output: 0 } } } }
/** A minimal head carrying the given billed usage (and a user-only composition). */
function billedHead(tokens: number, cost?: SessionCostUsage): Record<string, unknown> {
return timelineOf({
current: { system: 0, tools: 0, user: tokens, inject: 0, skill: 0, assistant: 0, tool: 0, total: tokens },
...(cost !== undefined ? { cost } : {}),
})
}
test('every descendant level folds into the root row’s family — members, cost, and days merge', () => {
const rows = rowsOfSnapshot(snapshotOf([
['root', {
displayTitle: 'root',
projectionValues: {
contextTimeline: billedHead(100, COST),
contextActivity: { days: { '2026-09-16': { tokens: 5, requests: 1, cost: COST, turns: 1, toolCalls: 1, toolsMs: 100, wallMs: 1_000 } } },
},
}],
['kid', {
origin: 'subagent', parentId: 'root',
projectionValues: {
contextTimeline: billedHead(50, COST2),
contextActivity: { days: { '2026-09-16': { tokens: 7, requests: 2, cost: COST2, turns: 1, toolCalls: 2, toolsMs: 200, wallMs: 2_000 } } },
},
}],
['grand', {
origin: 'subagent', parentId: 'kid',
projectionValues: {
contextTimeline: billedHead(25),
contextActivity: {
days: {
'2026-09-16': { tokens: 4, requests: 1, turns: 1, toolCalls: 3, toolsMs: 300, wallMs: 3_000 },
'2026-09-15': { tokens: 3, requests: 1, turns: 1, toolCalls: 4, toolsMs: 400, wallMs: 4_000 },
},
},
},
}],
['stranger', { displayTitle: 'unrelated' }],
]))
assert.ok(rows !== null)
assert.deepEqual(rows.map(r => r.id), ['root', 'stranger'], 'subagent-origin rows stay unlisted')
const [root, stranger] = rows
assert.equal(root.family.length, 3, 'self + kid + grand — all levels')
assert.equal(root.familyActivity?.length, 3, 'the per-member ledgers align with the family')
// COST (200) + COST2 (300) merge; the grandchild carried no cost.
assert.equal(billedOf(root), 500)
assert.equal(billedOf(stranger), null)
// The daily ledgers merge across the subtree, pricing records included.
assert.equal(root.activity?.days['2026-09-16'].tokens, 16)
assert.equal(root.activity?.days['2026-09-16'].requests, 4)
assert.deepEqual(root.activity?.days['2026-09-16'].cost, mergeCostUsage(COST, COST2))
assert.equal(root.activity?.days['2026-09-16'].turns, 3)
assert.equal(root.activity?.days['2026-09-16'].toolCalls, 6)
assert.equal(root.activity?.days['2026-09-16'].toolsMs, 600)
assert.equal(root.activity?.days['2026-09-16'].wallMs, 6_000)
assert.equal(root.activity?.days['2026-09-15'].tokens, 3)
assert.equal(root.activity?.days['2026-09-15'].turns, 1)
assert.equal(root.activity?.days['2026-09-15'].toolCalls, 4)
assert.equal(root.activity?.days['2026-09-15'].toolsMs, 400)
assert.equal(root.activity?.days['2026-09-15'].wallMs, 4_000)
assert.equal(root.activity?.days['2026-09-15'].cost, undefined)
// The composed split proportions each member's OWN composition: the root's
// all-user 160 billed input + output 40, the kid's all-user 300 — user 460 / output 40.
const parts = tokenPartsOf([root])
assert.deepEqual(parts, {
total: 500,
parts: [
{ key: 'user', color: 'var(--color-green-500)', value: 460 },
{ key: 'output', color: 'var(--color-pink-500)', value: 40 },
],
})
})
test('the daily tallies merge additively; a day none carried leaves them absent', () => {
const rows = rowsOfSnapshot(snapshotOf([
['root', {
displayTitle: 'root',
projectionValues: {
contextTimeline: billedHead(1),
contextActivity: {
days: {
'2026-09-14': { tokens: 1, requests: 1 },
'2026-09-13': { tokens: 1, requests: 1 },
},
},
},
}],
['kid', {
origin: 'subagent', parentId: 'root',
projectionValues: {
contextTimeline: billedHead(1),
contextActivity: {
days: {
'2026-09-14': { tokens: 1, requests: 1, turns: 1, toolCalls: 1, toolsMs: 1, wallMs: 1 },
'2026-09-13': { tokens: 1, requests: 1 },
},
},
},
}],
['grand', {
origin: 'subagent', parentId: 'kid',
projectionValues: {
contextTimeline: billedHead(1),
contextActivity: {
days: {
'2026-09-14': { tokens: 1, requests: 1 },
'2026-09-13': { tokens: 1, requests: 1 },
},
},
},
}],
]))
assert.ok(rows !== null)
const day = rows[0].activity?.days['2026-09-14']
assert.equal(day?.turns, 1)
assert.equal(day?.toolCalls, 1)
assert.equal(day?.toolsMs, 1)
assert.equal(day?.wallMs, 1)
const bare = rows[0].activity?.days['2026-09-13']
assert.equal(bare?.turns, undefined)
assert.equal(bare?.toolCalls, undefined)
assert.equal(bare?.toolsMs, undefined)
assert.equal(bare?.wallMs, undefined)
})
test('an unlisted subtree (a subagent whose root never lists) leaks nowhere', () => {
const rows = rowsOfSnapshot(snapshotOf([
['root', { displayTitle: 'root', projectionValues: { contextTimeline: billedHead(1, COST) } }],
['ghost', { origin: 'subagent', parentId: 'missing', projectionValues: { contextTimeline: billedHead(999, COST2) } }],
]))
assert.ok(rows !== null)
assert.equal(rows.length, 1)
assert.equal(billedOf(rows[0]), 200, 'the ghost subtree folds into no row')
})
test('a blank placeholder’s subtree stays out, and a lineage cycle cannot loop the walk', () => {
const rows = rowsOfSnapshot(snapshotOf([
// The reachable cycle: root points at p and p points back — the walk
// must cut at the revisited id (root counts its family, never itself twice).
['root', { displayTitle: 'root', parentId: 'p', projectionValues: { contextTimeline: billedHead(1, COST) } }],
['p', { origin: 'subagent', parentId: 'root', projectionValues: { contextTimeline: billedHead(2, COST2) } }],
['blank', { blank: true, parentId: 'root', projectionValues: { contextTimeline: billedHead(9, COST2) } }],
['under-blank', { origin: 'subagent', parentId: 'blank', projectionValues: { contextTimeline: billedHead(9, COST2) } }],
]))
assert.ok(rows !== null)
assert.equal(rows.length, 1)
assert.equal(rows[0].family.length, 2, 'root + p once; the blank is no agent and its child never links through')
assert.equal(billedOf(rows[0]), 500, 'COST + COST2 — the cycle’s revisit adds nothing twice')
})
test('the daily ledgers’ skill tables merge name by name (loads sum, last takes the max)', () => {
const rows = rowsOfSnapshot(snapshotOf([
['root', {
displayTitle: 'root',
projectionValues: {
contextActivity: { days: { '2026-09-16': { tokens: 5, requests: 1, skills: { tdd: { n: 2, last: 100 }, solo: { n: 1, last: 90 } } } } },
},
}],
['kid', {
origin: 'subagent', parentId: 'root',
projectionValues: {
contextActivity: { days: { '2026-09-16': { tokens: 7, requests: 2, skills: { tdd: { n: 3, last: 200 }, fresh: { n: 1, last: 300 } } } } },
},
}],
]))
assert.ok(rows !== null)
assert.deepEqual(rows[0].activity?.days['2026-09-16'].skills, {
tdd: { n: 5, last: 200 },
solo: { n: 1, last: 90 },
fresh: { n: 1, last: 300 },
})
})
test('hostile descendant projections degrade to null members, never a throw', () => {
const rows = rowsOfSnapshot(snapshotOf([
['root', { displayTitle: 'root', projectionValues: { contextTimeline: billedHead(1, COST) } }],
['kid', { origin: 'subagent', parentId: 'root', projectionValues: { contextTimeline: 7, contextActivity: 'x' } }],
['junk', 5 as unknown as Record<string, unknown>],
]))
assert.ok(rows !== null)
assert.equal(rows[0].family.length, 2)
assert.equal(rows[0].family[1], null, 'the hostile head sanitizes to null in place')
assert.equal(rows[0].activity, null, 'no member carried a ledger → the family has none')
assert.equal(billedOf(rows[0]), 200)
})
})
describe('coveredRowsOf', () => {
test('all passes the list through; the narrow scopes split on the archived tag', () => {
const rows = [
{ id: 'a', archived: false },
{ id: 'b', archived: true },
{ id: 'c', archived: true },
] as unknown as Parameters<typeof coveredRowsOf>[0]
assert.equal(coveredRowsOf(rows, 'all'), rows)
assert.deepEqual(coveredRowsOf(rows, 'active').map(r => r.id), ['a'])
assert.deepEqual(coveredRowsOf(rows, 'archived').map(r => r.id), ['b', 'c'])
assert.equal(coveredRowsOf([], 'archived').length, 0)
assert.deepEqual(coveredRowsOf([], 'all'), [])
})
})