fix(tui): search session references by title

This commit is contained in:
Yichen Jiang
2026-07-28 11:05:53 +08:00
parent e7bf303827
commit a43a2032ec
13 changed files with 183 additions and 51 deletions

View File

@@ -0,0 +1,24 @@
terminal 96x36 buffer=normal length=36 base=0 viewport=0
lifecycle started=1 stopped=0 progress=inactive
title "DSH snapshot"
cursor hidden column=8 viewportRow=4 bufferRow=4
viewport
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-blue bold
style 10-16 bold
1| " Snapshot agent ready."
style 1-21 fg=bright-black
2| " deepseek-v4-flash • main-session"
style 1-34 dim
3| "────────────────────────────────────────────────────────────────────────────────────────────────"
style 0-95 dim
4| " @design "
style 8-8 inverse
5| "────────────────────────────────────────────────────────────────────────────────────────────────"
style 0-95 dim
6| " → Session · Searchable design re opaque-source-id · /workspace/project · 1970-01-01T00:00:0 "
style 1-32 fg=bright-blue
7| "deepseek-v4-flash /workspace/project ↑0 ↓0 0% context tools:collapsed"
style 0-43 dim
style 69-95 dim
8-35| <blank>

View File

@@ -8,6 +8,7 @@ import { agentEvents } from '@deepseek-ai/dsh-agent'
import { CallId, ReasoningEffortId, type ContentBlock } from '@deepseek-ai/dsh-llm'
import type {} from '@deepseek-ai/dsh-llm-retry'
import { SessionId, type JsonValue, type Session } from '@deepseek-ai/dsh-session'
import SessionReferenceService from '@deepseek-ai/dsh-session-reference'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry, { type ToolDefinition, type ToolResultView } from '@deepseek-ai/dsh-tools'
import * as ToolCordis from '@deepseek-ai/dsh-tool-cordis'
@@ -21,6 +22,7 @@ import {
type TuiHarnessOptions,
} from './harness.ts'
import { HeadlessTerminal, type TerminalSnapshotOptions } from './headless-terminal.ts'
import { TestSessionQueryService } from './session-query.ts'
const SNAPSHOTS_DIR = join(dirname(fileURLToPath(import.meta.url)), 'snapshots')
const REFRESHING = process.env.DSH_SNAPSHOT === 'refresh'
@@ -33,6 +35,7 @@ const CHECKPOINTS = [
'retry-exhausted',
'banner-gradient',
'file-autocomplete',
'session-title-autocomplete',
'code-mode-pending',
'dynamic-workflow-pending',
'cordis-tools-pending',
@@ -370,6 +373,30 @@ describe('TUI terminal-state snapshots', () => {
}
})
it('pins session autocomplete discovered through a log-backed title', async () => {
const harness = await setupSnapshot({
async configureContext(ctx) {
ctx.provide('tools', { get: () => undefined } as never)
await ctx.plugin(TestSessionQueryService)
await ctx.plugin(SessionReferenceService)
const source = ctx.sessions.create(SessionId('opaque-source-id'), {
meta: { cwd: '/workspace/project', createdAt: 1 },
})
source.append('session/title', {
title: 'Searchable design review',
messageSeqs: [],
source: { kind: 'fallback' },
})
},
})
harness.terminal.send('@design')
await vi.waitFor(async () => {
expect(await harness.terminal.snapshot()).toContain('Session · Searchable design re')
})
await checkpoint('session-title-autocomplete', harness.terminal)
await disposeSnapshot(harness)
})
it('pins Code Mode run_code with its production presenter', async () => {
const harness = await setupSnapshot({ configureContext: configureAdvancedTools })
const call = {

View File

@@ -1834,21 +1834,50 @@ describe('pi-tui chat lifecycle and transcript', () => {
})
it('combines session autocomplete with files and prepares send/steer references asynchronously', async () => {
let sourceId = SessionId('uninitialized')
const sourceId = SessionId('source-session')
const sourceHeader: SessionHeader = {
version: 0,
id: sourceId,
cwd: '/workspace',
createdAt: 1,
}
const noCwdHeader: SessionHeader = {
version: 0,
id: SessionId('no-cwd'),
createdAt: 2,
}
const sourceEvents: SessionEvent[] = [
{
type: 'user/message',
seq: 0,
time: 1,
data: { content: [{ type: 'text', text: 'source background' }], source: { kind: 'user' } },
surfaceOp: 'append',
},
{
type: 'session/title',
seq: 1,
time: 2,
data: {
title: 'Source chat',
messageSeqs: [0],
source: { kind: 'fallback' },
},
},
]
const result = await setup({
sessionPersistence: {
list: async () => [noCwdHeader, sourceHeader],
load: async (id) => {
if (id === sourceId) return { meta: sourceHeader, events: sourceEvents }
if (id === noCwdHeader.id) return { meta: noCwdHeader, events: [] }
throw new Error(`unexpected persisted session ${id}`)
},
},
async configureContext(ctx) {
ctx.provide('tools', { get: () => undefined } as never)
await ctx.plugin(TestSessionQueryService)
await ctx.plugin(SessionReferenceService)
const source = ctx.sessions.create(SessionId('source-session'), { meta: { cwd: process.cwd(), createdAt: 1 } })
sourceId = source.id
appendUser(source, 'source background')
source.append('session/title', {
title: 'Source chat',
messageSeqs: [0],
source: { kind: 'fallback' },
})
ctx.sessions.create(SessionId('no-cwd'), { meta: { createdAt: 2 } })
},
})
@@ -1857,7 +1886,7 @@ describe('pi-tui chat lifecycle and transcript', () => {
expect(result.terminal.output).toContain('(no cwd)')
result.terminal.send('\x03')
result.terminal.send('@source-session')
result.terminal.send('@chat')
await vi.waitFor(() => { expect(result.terminal.output).toContain('Session · Source chat') })
expect(result.terminal.output).toContain('source-session')
result.terminal.send('\t')