fix(web): address skill row review feedback

This commit is contained in:
Yichen Jiang
2026-08-06 17:21:13 +08:00
parent 690f7ae035
commit a667d2cd64
39 changed files with 326 additions and 142 deletions

View File

@@ -3,7 +3,7 @@
// deferred-controlled timing). Streams are hand pumps: pushMux/pushHost.
import type { CommandId } from '@deepseek-ai/dsh-commands/brand'
import type {
ClientResponse, CommandDescriptor, HostFrame, IApiClient, ModelTarget, MuxFrame,
ClientResponse, CommandDescriptor, HistoryEntry, HostFrame, IApiClient, ModelTarget, MuxFrame,
RpcError, RpcReceipt, RpcRequest, RpcResponse, SessionId, SessionModels, SessionSearchItem, SkillEntry,
WorkspaceId, WorkspaceView,
} from '@deepseek-ai/dsh-client-connection/client'
@@ -68,7 +68,7 @@ export class FakeApiClient implements IApiClient {
onRename: (payload: unknown) => Promise<RpcResponse<{ title: string; seq: number }>> = () => Promise.resolve(ok({ title: 'fk-renamed', seq: 0 }))
onFork: (payload: unknown) => Promise<RpcResponse<{ sessionId: SessionId }>> = () => Promise.resolve(ok({ sessionId: 'fk-fork' as SessionId }))
onHistory: (payload: { sessionId: SessionId; beforeSeq?: number; maxMessages?: number })
=> Promise<RpcResponse<{ events: never[]; hasMore: boolean }>> =
=> Promise<RpcResponse<{ events: HistoryEntry[]; hasMore: boolean }>> =
() => Promise.resolve(ok({ events: [], hasMore: false }))
onModels: (payload: unknown) => Promise<RpcResponse<SessionModels>> = () => Promise.resolve(ok({

View File

@@ -53,6 +53,20 @@ describe('projectConversationHistory', () => {
}])
})
it('projects a paged tool result from its host-carried call pair', () => {
const result = ev.toolResult(50, 3, 'outside-call', '已加载 skill')
const projection = projectConversationHistory([{
event: result,
call: { name: 'skill', arguments: '{"name":"dsh-code-review"}', time: 40 },
}])
expect(projection.eventNodes).toMatchObject([{
kind: 'tool-result',
call: { name: 'skill', argsRaw: '{"name":"dsh-code-review"}' },
callTime: 40,
callView: null,
}])
})
it('projects a high-sequence history window without synthesizing its unloaded prefix', () => {
const baseSeq = 400_000
const events = [

View File

@@ -53,6 +53,23 @@ describe('open', () => {
expect(snapshot.turnEnds.get(3)).toBe(15)
})
it('installs host-carried call metadata for a result-only tail page', async () => {
const { api, session } = makeSession()
api.onHistory = () => Promise.resolve(ok({
events: [{
event: ev.toolResult(50, 3, 'outside-call', '已加载 skill'),
call: { name: 'skill', arguments: '{"name":"dsh-code-review"}', time: 40 },
}],
hasMore: true,
}))
await session.open()
expect(session.getSnapshot().nodes).toMatchObject([{
kind: 'tool-result',
call: { name: 'skill', argsRaw: '{"name":"dsh-code-review"}' },
callTime: 40,
}])
})
it('is idempotent: concurrent opens share one history call, reopening when open is a no-op', async () => {
const { api, session } = makeSession()
await Promise.all([session.open(), session.open()])

View File

@@ -365,6 +365,22 @@ describe('TranscriptAdapter', () => {
expect(adapter.nodes()[0]).toMatchObject({ kind: 'tool-result', callId: 'outside-call', call: null })
})
it('materializes a paged tool-result from its host-carried call pair', () => {
const adapter = new TranscriptAdapter()
adapter.reset(
[ev.toolResult(50, 3, 'outside-call', '已加载 skill')],
[undefined],
[{ name: 'skill', arguments: '{"name":"dsh-code-review"}', time: 40 }],
)
expect(adapter.nodes()[0]).toMatchObject({
kind: 'tool-result',
callId: 'outside-call',
call: { name: 'skill', argsRaw: '{"name":"dsh-code-review"}' },
callTime: 40,
callView: null,
})
})
it('materializes a tool-result error field when present', () => {
const adapter = new TranscriptAdapter()
adapter.reset([