Files
dsh-context/tests/host/fold-split.spec.ts
T
bowenliang123 9bec22959b feat(timing): the session-time strip — the session's active time as one packed band
The timing card gains a bottom band that reads where the session's ACTIVE
time went: every completed step's slices — the TTFT wait, the decode blocks
in stream order, the tool-run windows, and the in-step residue — packed
gapless in occurrence order, each band's width its share of the cumulative
active time (idle time between steps takes no track), with ticks at zero,
the quartiles, and the full span.

Host: the fold tiles each step at step/end (clamped into the step window,
first-wins de-overlap so parallel runs paint their union, holes filled with
the residue kind) into a new spans collection — bounded to the newest 2,000,
riding the detail channel and the inline view, never the slim head. The
decode window anchors at the first OBSERVABLE instant (the first token, or
an earlier block marker the token packing could not stamp), so a redacted
reasoning block keeps its span instead of being swallowed by the wait.
stateVersion 22 → 23: the per-step collection cannot be backfilled by later
events, so cached rows refold from the durable log.

Client: the strip joins the card's hover link by KIND — a band hover lights
every span of its kind plus the donut arc and the legend row, and the
floating tip answers only the strip's own pointer (the mirror rule).
2026-09-30 17:56:09 +08:00

222 lines
10 KiB
TypeScript

// The split wire generation (src/host/fold.ts buildTimelineHead /
// buildTimelineDetail + the detailRev ledger): the slim head carries the
// counters and the headline anchor while the detail payload serves the heavy
// collections, and the revision bumps exactly on detail-mutating folds.
// Also pins the equivalence: inline view = head + detail, so the two
// generations can never drift apart.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
import { buildTimelineDetail, buildTimelineHead } from '../../src/host/fold'
import type { TimelineEvent } from '../../src/host/fold'
import { resolveBounds } from '../../src/host/config'
import {
assistantMessage,
compaction,
header,
planMode,
requestContext,
stepEnd,
stepStart,
systemMessage,
toolCall,
toolResult,
userMessage,
} from './helpers/events'
import { assertPlainJson, driveTimeline, timelineDef } from './helpers/projection'
/** A session log touching every envelope family the fold serves (mirrors the compat driver's canonical log). */
function canonicalLog(): TimelineEvent[] {
return [
header(1, {
tools: [{ name: 'bash', description: 'run a command' }],
model: 'deepseek-v4-flash',
provider: 'deepseek',
}),
systemMessage(2),
requestContext(3, { contextWindow: 128000 }),
stepStart(4),
userMessage(5, [{ type: 'text', text: 'hello there' }], { kind: 'user' }),
assistantMessage(6, { turn: 1, step: 0, usage: { inputTokens: 10, outputTokens: 5, cacheReadTokens: 3 } }),
toolCall(7, { callId: 'c1', name: 'bash' }),
toolResult(8, { callId: 'c1', content: [{ type: 'text', text: 'ok' }] }),
stepEnd(9),
assistantMessage(10, { turn: 1, step: 1, usage: { inputTokens: 20, outputTokens: 8 } }),
compaction(11, 'summary', { shadowedTokenCount: 12, shadowedSeqs: [5] }),
planMode(12, { active: true }),
]
}
describe('the detailRev ledger', () => {
test('bumps exactly on the detail-mutating folds of the canonical log', () => {
// Mutations: userMessage(5), assistantMessage(6), toolResult(8),
// stepEnd(9, the strip's span flush), assistantMessage(10), compaction(11),
// planMode(12) — seven in total.
const { state } = driveTimeline(canonicalLog())
assert.equal(state.detailRev, 7)
})
test('the working-slot and envelope folds do NOT bump (nothing to refetch)', () => {
const { def, state } = driveTimeline(canonicalLog())
// step/start (arms the step slot), tool/call (arms the call ledger),
// request/context (capacity metadata), and a header with an UNCHANGED
// model all leave the detail collections as-is.
const follow: TimelineEvent[] = [
requestContext(13, { contextWindow: 256000 }),
header(14, { model: 'deepseek-v4-flash', provider: 'deepseek', reason: 'change' }),
stepStart(15),
toolCall(16, { callId: 'c2', name: 'bash' }),
]
let st = state
for (const ev of follow) st = def.apply(st, ev)
assert.equal(st.detailRev, 7, 'no detail mutation, no bump')
// The capacity metadata and the header DID fold (state changed) — only the rev stayed.
assert.notEqual(st, state)
})
test('a real model switch bumps (the events collection gains a row)', () => {
const { def, state } = driveTimeline(canonicalLog())
const switched = def.apply(state, header(13, { model: 'deepseek-v4-pro', provider: 'deepseek', reason: 'change' }))
assert.equal(switched.detailRev, 8)
})
test('the rev is plain JSON and survives the state gate', () => {
const { states } = driveTimeline(canonicalLog())
for (const state of states) assertPlainJson(state)
})
})
describe('buildTimelineHead', () => {
test('serves the counters, the headline anchor, and the revision — with empty collections', () => {
const { state } = driveTimeline(canonicalLog())
const head = buildTimelineHead(state)
assert.equal(head.ok, true)
assert.equal(head.model, 'deepseek-v4-flash')
assert.equal(head.provider, 'deepseek')
assert.equal(head.contextWindow, 128000)
assert.ok(head.current.total > 0)
// The retained records tally: one turn, two steps, the compaction event.
assert.deepEqual(head.counts, { turns: 1, steps: 2, injects: 0, compactions: 1, prunes: 0 })
// The whole-session tally rides the head too: the canonical log's one
// user message (injections excluded).
assert.equal(head.humanInputs, 1)
// The card preview rides the head: the canonical log's own user message.
assert.equal(head.lastUser, 'hello there')
// The headline anchor is the newest retained request's billing summary.
const lastReq = state.requests.at(-1)!
assert.deepEqual(head.last, { seq: lastReq.seq, total: lastReq.total, prompt: 20 })
assert.equal(head.detailRev, 7)
// The collections stay empty on the head — the detail channel serves them.
assert.deepEqual(head.requests, [])
assert.deepEqual(head.events, [])
assert.deepEqual(head.nodes, [])
assert.equal(head.droppedNodes, 0)
assert.deepEqual(head.archive, [])
assertPlainJson(head)
})
test('the counters tally injects/prunes, and a turn-less record folds as turn 0', () => {
const { state } = driveTimeline([
userMessage(1, [{ type: 'text', text: 'ctx' }], { kind: 'plugin', form: 'context', plugin: 'dsh-test' }),
compaction(2, 'prune', { shadowedTokenCount: 3 }),
assistantMessage(3, {}), // no turn: the replay shape folds as turn 0
])
const head = buildTimelineHead(state)
assert.deepEqual(head.counts, { turns: 1, steps: 1, injects: 1, compactions: 0, prunes: 1 })
})
test('a fresh state serves zeroed counters and no last/detailRev payload keys beyond the marker', () => {
const head = buildTimelineHead(timelineDef().init())
assert.deepEqual(head.counts, { turns: 0, steps: 0, injects: 0, compactions: 0, prunes: 0 })
assert.equal(head.humanInputs, 0, 'the tally defaults to zero on the wire')
assert.equal(head.lastUser, undefined, 'no user message yet — the preview stays absent')
assert.ok(!('lastUser' in head), 'no own key on the wire')
assert.equal(head.last, undefined, 'no request yet — the anchor stays absent')
assert.equal(head.detailRev, 0)
assert.equal(head.current.total, 0)
})
test('the newest request without a usage sample anchors without prompt', () => {
const { state } = driveTimeline([
userMessage(1, [{ type: 'text', text: 'hi' }], { kind: 'user' }),
assistantMessage(2, { turn: 1, step: 1 }),
])
const head = buildTimelineHead(state)
assert.deepEqual(head.last, { seq: 2, total: state.requests[0].total })
assert.ok(head.last !== undefined && !('prompt' in head.last), 'no undefined-valued prompt key')
})
})
describe('buildTimelineDetail', () => {
test('serves the same collections the inline view serves, plus the revision and the head', () => {
const { state, view } = driveTimeline(canonicalLog())
const detail = buildTimelineDetail(state, resolveBounds({}))
assert.equal(detail.rev, 7)
assert.deepEqual(detail.requests, view.requests)
assert.deepEqual(detail.events, view.events)
assert.deepEqual(detail.nodes, view.nodes)
assert.equal(detail.droppedNodes, view.droppedNodes)
assert.deepEqual(detail.archive, view.archive)
assert.equal(detail.surfaceFloor, view.surfaceFloor)
assert.equal(detail.archiveFloor, view.archiveFloor)
// The slim head rides at the SAME cut: the composition the Agent
// network's cold-node ring fetch renders from.
assert.ok(detail.head !== undefined)
assert.deepEqual(detail.head.current, view.current)
assert.deepEqual(detail.head.counts, buildTimelineHead(state).counts)
assertPlainJson(detail)
// The detail never aliases the persisted state.
assert.notEqual(detail.requests[0], state.requests[0])
assert.notEqual(detail.archive, state.archived)
})
test('the served window and floors follow the retention bounds (same rule as the inline view)', () => {
const events: TimelineEvent[] = [
userMessage(1, [{ type: 'text', text: 'injected' }], { kind: 'plugin', form: 'context', plugin: 'dsh-test' }),
userMessage(2, [{ type: 'text', text: 'x1' }]),
userMessage(3, [{ type: 'text', text: 'x2' }]),
userMessage(4, [{ type: 'text', text: 'x3' }]),
userMessage(5, [{ type: 'text', text: 'x4' }]),
]
const { state } = driveTimeline(events)
const detail = buildTimelineDetail(state, resolveBounds({ maxNodes: 2 }))
assert.deepEqual(detail.nodes.map(n => n.seq), [1, 4, 5], 'the out-of-window inject is pinned ahead of the tail')
assert.equal(detail.droppedNodes, 2)
assert.equal(detail.surfaceFloor, 3)
assert.equal(detail.rev, 5)
})
})
describe('the split wire generation (definition slim flag)', () => {
test('the slim view validates against the wire schema and carries the marker', () => {
const slim = timelineDef({}, true)
const { state } = driveTimeline(canonicalLog())
const view = slim.wire.view(state)
assert.equal(slim.wire.viewSchema.safeParse(view).success, true, 'the slim head validates')
assert.equal(typeof view.detailRev, 'number', 'the split marker is present')
assert.ok(view.counts !== undefined)
assert.deepEqual(view.nodes, [], 'collections stay off the wire value')
})
test('the inline view carries the collections and NO split markers', () => {
const inline = timelineDef({}, false)
const { state } = driveTimeline(canonicalLog())
const view = inline.wire.view(state)
assert.equal(inline.wire.viewSchema.safeParse(view).success, true, 'the inline value validates on the same schema')
assert.equal(view.detailRev, undefined)
assert.equal(view.counts, undefined)
assert.ok(view.nodes.length > 0)
assert.ok(view.requests.length > 0)
})
test('equivalence: the inline view is exactly the head plus the detail (minus the markers)', () => {
const { state } = driveTimeline(canonicalLog())
const head = buildTimelineHead(state)
const detail = buildTimelineDetail(state, resolveBounds({}))
const inline = timelineDef({}, false).wire.view(state)
const { counts: _counts, last: _last, detailRev: _rev, ...headScalars } = head
const { rev: _detailRev, head: _detailHead, ...collections } = detail
assert.deepEqual(inline, { ...headScalars, ...collections })
})
})