mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
UI rebuild (desktop): - TraceSession: replace 3-column layout with two panes — turn-grouped timeline tree (draggable splitter, search/filter, keyboard nav) and a section-flow detail panel (Response / Messages / System Prompt / Tools / Parameters / Raw), collapse state persists across spans - Render LLM requests/responses semantically: messages as role-colored conversation with tool_use/tool_result pairing instead of raw JSON dumps; Raw fallback via CodeViewer for legacy truncated records - TraceList: row-style list with model chips, mono metrics, hover actions; content-visibility rows (no virtualization, WebKit-safe) - i18n synced across zh/en/jp/kr/zh-TW (+25/-55 keys) Data & capture (server): - Capture full bodies: preview cap 2048 -> 240k chars, stream cap 256KB -> 1MB; list API trims previews to keep polling light; new GET /api/sessions/:id/trace/calls/:callId returns the full record - Extract per-call token usage at read time (SSE + JSON + proxy wrapped); mtime-keyed read cache for the polling path - Fix sensitive-key regex redacting *_tokens count fields, which made token stats always report 0 Frontend data layer: - SSE stream reassembly (Anthropic + OpenAI chat) adapted from claude-tap (MIT, attribution in THIRD_PARTY_LICENSES.md), request/ response body parsers, shared formatters, on-demand call detail cache; traceViewModel gains tokenUsage/isLifecycleNoise, drops fullRaw Tested: - bun run check:server (1201 pass) - bun run check:desktop (lint + 1358 tests + build) - Chromium walkthrough against real local traces: list, session tree, LLM semantic detail (new format), legacy fallback, tool detail
60 lines
1.9 KiB
TypeScript
60 lines
1.9 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { setBaseUrl } from './client'
|
|
import { sessionsApi } from './sessions'
|
|
|
|
describe('sessionsApi', () => {
|
|
afterEach(() => {
|
|
setBaseUrl('http://127.0.0.1:3456')
|
|
vi.restoreAllMocks()
|
|
})
|
|
|
|
it('posts branch requests to the session branch endpoint', async () => {
|
|
const fetchMock = vi.spyOn(globalThis, 'fetch')
|
|
fetchMock.mockResolvedValueOnce(new Response(JSON.stringify({
|
|
sessionId: 'branch-session',
|
|
title: 'Branch',
|
|
workDir: '/workspace/repo',
|
|
sourceSessionId: 'source-session',
|
|
targetMessageId: 'message-1',
|
|
}), {
|
|
status: 201,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}))
|
|
|
|
setBaseUrl('http://127.0.0.1:49237')
|
|
const result = await sessionsApi.branch('source-session', {
|
|
targetMessageId: 'message-1',
|
|
title: 'Branch',
|
|
})
|
|
|
|
expect(result.sessionId).toBe('branch-session')
|
|
expect(fetchMock).toHaveBeenCalledOnce()
|
|
const [url, init] = fetchMock.mock.calls[0]!
|
|
expect(url).toBe('http://127.0.0.1:49237/api/sessions/source-session/branch')
|
|
expect(init).toMatchObject({
|
|
method: 'POST',
|
|
body: JSON.stringify({
|
|
targetMessageId: 'message-1',
|
|
title: 'Branch',
|
|
}),
|
|
})
|
|
})
|
|
|
|
it('fetches a single trace call from the call detail endpoint', async () => {
|
|
const fetchMock = vi.spyOn(globalThis, 'fetch')
|
|
fetchMock.mockResolvedValueOnce(new Response(JSON.stringify({
|
|
call: { id: 'call-1', sessionId: 'session-1' },
|
|
}), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}))
|
|
|
|
const result = await sessionsApi.getTraceCall('session-1', 'call-1')
|
|
|
|
expect(result.call.id).toBe('call-1')
|
|
const [url, init] = fetchMock.mock.calls[0]!
|
|
expect(url).toBe('http://127.0.0.1:3456/api/sessions/session-1/trace/calls/call-1')
|
|
expect(init).toMatchObject({ method: 'GET' })
|
|
})
|
|
})
|