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

@@ -7,7 +7,7 @@
export type {
ApiProxy, SessionsApi, SessionSearchItem, SessionSummary, HostApi, EventsApi, MuxFrame, HostFrame,
ApprovalResponsePayload, QuestionResponsePayload, HistoryEntry, ToolEventView,
ApprovalResponsePayload, QuestionResponsePayload, HistoryEntry, HistoryToolCall, ToolEventView,
DirectoryEntry, DirectoryListing,
WorkspaceApi, WorkspaceId, WorkspaceView,
CommandsApi, CommandDescriptor, SkillsApi, SkillEntry,

View File

@@ -29,7 +29,7 @@ import type {
import type { CommandId } from '@deepseek-ai/dsh-commands/brand'
import { deriveEventMessage, foldSurface } from '@deepseek-ai/dsh-session/surface'
import type {
ApiProxy, ClientRequest, ClientResponse, HistoryEntry, HostFrame, MuxFrame, RpcReceipt,
ApiProxy, ClientRequest, ClientResponse, HistoryEntry, HistoryToolCall, HostFrame, MuxFrame, RpcReceipt,
ModelProviderGroup, ModelTarget, RpcRequest, RpcResponse, RpcResult, ServerRequest, ServerResponse, SessionSummary,
ToolCallView, ToolEventView, ToolResultView, WorkspaceId, WorkspaceView,
} from './api.ts'
@@ -661,25 +661,33 @@ function presentResult(name: string, argsRaw: string, resultText: string): ToolR
}
}
/** Host-side viewFor mirror: tool/call presents from its own args; tool/result back-scans the log for the paired call. */
/** Full-log tool/result pair used by the fixture history envelope and presenter mirror. */
function pairedHistoryCall(event: SessionEvent, log: readonly SessionEvent[]): HistoryToolCall | undefined {
if (event.type !== 'tool/result') return undefined
const callId = String(event.data.message.source.callId)
for (let i = log.length - 1; i >= 0; i--) {
const candidate = log[i]
/* v8 ignore next -- dense-array guard: i stays within [0, log.length),
so the undefined arm needs a sparse log no code path builds. */
if (candidate !== undefined && candidate.type === 'tool/call' && String(candidate.data.callId) === callId) {
return { name: candidate.data.name, arguments: candidate.data.arguments, time: candidate.time }
}
}
return undefined
}
/** Host-side viewFor mirror: tool/call presents from its own args; tool/result uses its full-log pair. */
function viewFor(event: SessionEvent, log: readonly SessionEvent[]): ToolEventView | undefined {
if (event.type === 'tool/call') {
const view = presentCall(event.data.name, event.data.arguments)
return view === undefined ? undefined : { for: 'call', view }
}
if (event.type === 'tool/result') {
const callId = String(event.data.message.source.callId)
for (let i = log.length - 1; i >= 0; i--) {
const candidate = log[i]
/* v8 ignore next -- dense-array guard: i stays within [0, log.length),
so the undefined arm needs a sparse log no code path builds. */
if (candidate !== undefined && candidate.type === 'tool/call' && String(candidate.data.callId) === callId) {
const resultText = event.data.message.content[0].content.map(b => (b.type === 'text' ? b.text : '')).join('')
const view = presentResult(candidate.data.name, candidate.data.arguments, resultText)
return view === undefined ? undefined : { for: 'result', view }
}
}
return undefined // cross-page unpaired: documented default
const call = pairedHistoryCall(event, log)
if (call === undefined) return undefined
const resultText = event.data.message.content[0].content.map(b => (b.type === 'text' ? b.text : '')).join('')
const view = presentResult(call.name, call.arguments, resultText)
return view === undefined ? undefined : { for: 'result', view }
}
return undefined
}
@@ -1044,7 +1052,12 @@ function pageOf(
}
const events = log.slice(start, end).map((event): HistoryEntry => {
const view = viewFor(event, log)
return view === undefined ? { event } : { event, view }
const call = pairedHistoryCall(event, log)
return {
event,
...view === undefined ? {} : { view },
...call === undefined ? {} : { call },
}
})
return { events, hasMore: start > 0 }
}

View File

@@ -13,7 +13,7 @@ import { isLoopbackHostname } from '../loopback-hostname.ts'
// ---- Contract re-exports (browser-safe apiproxy channels + core types) ----
export type {
ApiProxy, SessionsApi, SessionSearchItem, SessionSummary, HostApi, EventsApi, MuxFrame, HostFrame,
ApprovalResponsePayload, QuestionResponsePayload, HistoryEntry, ToolEventView,
ApprovalResponsePayload, QuestionResponsePayload, HistoryEntry, HistoryToolCall, ToolEventView,
DirectoryEntry, DirectoryListing,
ToolCallView, ToolResultView, WorkspaceApi, WorkspaceId, WorkspaceView,
CommandsApi, CommandDescriptor, SkillsApi, SkillEntry,