fix(web): address skill row review feedback
This commit is contained in:
39 files changed
+326
-142
No files matched your search
@@ -362,7 +362,8 @@ export function projectConversationHistory(
|
||||
let contextGeneration = 0
|
||||
|
||||
for (const [index, event] of events.entries()) {
|
||||
const view = entries[index]?.view
|
||||
const entry = entries[index]
|
||||
const view = entry?.view
|
||||
if (event.type === 'tool/call') {
|
||||
callIndex.set(String(event.data.callId), {
|
||||
name: event.data.name,
|
||||
@@ -370,8 +371,17 @@ export function projectConversationHistory(
|
||||
time: event.time,
|
||||
callView: view?.for === 'call' ? view.view : null,
|
||||
})
|
||||
} else if (event.type === 'tool/result' && view?.for === 'result') {
|
||||
resultViews.set(event.seq, view.view)
|
||||
} else if (event.type === 'tool/result') {
|
||||
const callId = String(event.data.message.source.callId)
|
||||
if (!callIndex.has(callId) && entry?.call !== undefined) {
|
||||
callIndex.set(callId, {
|
||||
name: entry.call.name,
|
||||
argsRaw: entry.call.arguments,
|
||||
time: entry.call.time,
|
||||
callView: null,
|
||||
})
|
||||
}
|
||||
if (view?.for === 'result') resultViews.set(event.seq, view.view)
|
||||
}
|
||||
if (isSurfaceEvent(event) && event.surfaceOp !== 'append') {
|
||||
contextGeneration++
|
||||
|
||||
@@ -155,16 +155,16 @@ export interface TurnErrorNode {
|
||||
code?: string
|
||||
}
|
||||
|
||||
/** A tool result paired (when in-window) with its call head. */
|
||||
/** A tool result paired with its durable call head when the Host can resolve it. */
|
||||
export interface ToolResultNode {
|
||||
kind: 'tool-result'
|
||||
seq: number
|
||||
/** Unix epoch ms from the tool/result session event. */
|
||||
time: number
|
||||
callId: string
|
||||
/** Call head backfilled from the in-window tool/call; null when window truncation left the call outside (card head shows callId). */
|
||||
/** Call head from the window or history envelope; null only when the durable log has no pair (card head shows callId). */
|
||||
call: { name: string; argsRaw: string } | null
|
||||
/** Unix epoch ms of the paired tool/call when the call is still in-window; used for call-row duration. */
|
||||
/** Unix epoch ms of the paired tool/call; null when the durable log has no pair. */
|
||||
callTime: number | null
|
||||
content: readonly ContentBlock[]
|
||||
isError: boolean
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { ContentBlock } from '@deepseek-ai/dsh-llm/types'
|
||||
import type { LlmRetryEventData } from '@deepseek-ai/dsh-llm-retry/types'
|
||||
import type { SessionEvent } from '@deepseek-ai/dsh-session/types'
|
||||
import type {
|
||||
HistoryEntry, IApiClient, MessageId, MuxFrame, QueueAction, RpcError,
|
||||
HistoryEntry, HistoryToolCall, IApiClient, MessageId, MuxFrame, QueueAction, RpcError,
|
||||
RpcId, RpcResponse, RpcResult, SessionId, SubagentAddress, ToolEventView,
|
||||
} from '@deepseek-ai/dsh-client-connection/client'
|
||||
// Value import from the inline-safe wire layer (not the connection plugin):
|
||||
@@ -85,6 +85,8 @@ export class Session implements SessionFace {
|
||||
/** Wire views aligned with `events` by index (envelope-level annotations; undefined = no view).
|
||||
* Kept parallel rather than merged so `events` stays the raw log slice (model-visible ⟺ logged). */
|
||||
private views: (ToolEventView | undefined)[] = []
|
||||
/** Host-carried call metadata aligned with result entries when the call event is outside the page. */
|
||||
private historyCalls: (HistoryToolCall | undefined)[] = []
|
||||
private baseSeq = 0
|
||||
private hasMore = false
|
||||
private openState: OpenState = 'cold'
|
||||
@@ -381,10 +383,11 @@ export class Session implements SessionFace {
|
||||
}
|
||||
this.events = [...older.map(e => e.event), ...this.events]
|
||||
this.views = [...older.map(e => e.view), ...this.views]
|
||||
this.historyCalls = [...older.map(e => e.call), ...this.historyCalls]
|
||||
/* v8 ignore next -- the ?? arm needs older[0] undefined, but the empty-page branch above already returned. */
|
||||
this.baseSeq = older[0]?.event.seq ?? this.baseSeq
|
||||
this.hasMore = result.value.hasMore
|
||||
this.transcript.reset(this.events, this.views) // prepend forces a rebuild (the window grew at the head)
|
||||
this.transcript.reset(this.events, this.views, this.historyCalls) // prepend forces a rebuild (the window grew at the head)
|
||||
this.rebuildDerivedFromWindow()
|
||||
} catch (error) {
|
||||
console.error('[web-runtime] loadOlder failed:', error)
|
||||
@@ -411,6 +414,7 @@ export class Session implements SessionFace {
|
||||
this.openError = null
|
||||
this.events = []
|
||||
this.views = []
|
||||
this.historyCalls = []
|
||||
this.baseSeq = 0
|
||||
// Superseded, not settled: the baseline replay re-sends still-pending requested frames verbatim
|
||||
// (same rpcId), re-minting fresh waits; a stale reference's respond() still reaches the host.
|
||||
@@ -644,9 +648,10 @@ export class Session implements SessionFace {
|
||||
private installWindow(entries: HistoryEntry[], hasMore: boolean, projections?: ProjectionsBaseline): void {
|
||||
this.events = entries.map(e => e.event)
|
||||
this.views = entries.map(e => e.view)
|
||||
this.historyCalls = entries.map(e => e.call)
|
||||
this.baseSeq = this.events[0]?.seq ?? 0
|
||||
this.hasMore = hasMore
|
||||
this.transcript.reset(this.events, this.views)
|
||||
this.transcript.reset(this.events, this.views, this.historyCalls)
|
||||
this.rebuildDerivedFromWindow()
|
||||
if (projections !== undefined) this.projections.seed(projections)
|
||||
const buffered = this.liveBuffer
|
||||
@@ -661,6 +666,7 @@ export class Session implements SessionFace {
|
||||
if (tailSeq !== null && event.seq <= tailSeq) return // replay overlap, drop
|
||||
this.events.push(event)
|
||||
this.views.push(view)
|
||||
this.historyCalls.push(undefined)
|
||||
this.transcript.append(event, view)
|
||||
this.handoffPendingSteering(event)
|
||||
this.applyEventSideEffects(event, view)
|
||||
|
||||
@@ -19,7 +19,9 @@ import type { CommandId } from '@deepseek-ai/dsh-commands/brand'
|
||||
// `sessions: ISessions` (TS2717, the one-program-per-side rule in
|
||||
// docs/development.md).
|
||||
import type { COMPACT_CHECKPOINT_SOURCE } from '@deepseek-ai/dsh-compact/checkpoint'
|
||||
import type { ToolCallView, ToolEventView, ToolResultView } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import type {
|
||||
HistoryToolCall, ToolCallView, ToolEventView, ToolResultView,
|
||||
} from '@deepseek-ai/dsh-client-connection/client'
|
||||
import type { CommandNode, CompactionSummaryNode, ConversationNode } from './conversation.ts'
|
||||
import { toAssistantBlocks } from './conversation.ts'
|
||||
import { contextForm, contextProvenance } from './context-provenance.ts'
|
||||
@@ -213,8 +215,13 @@ export class TranscriptAdapter {
|
||||
* and re-project the transcript.
|
||||
* @param events - the new window contents (seq-ascending).
|
||||
* @param views - per-event wire views aligned with `events` by index (undefined slots for view-less events).
|
||||
* @param calls - host-carried result pairs aligned with `events` by index.
|
||||
*/
|
||||
reset(events: readonly SessionEvent[], views?: readonly (ToolEventView | undefined)[]): void {
|
||||
reset(
|
||||
events: readonly SessionEvent[],
|
||||
views?: readonly (ToolEventView | undefined)[],
|
||||
calls?: readonly (HistoryToolCall | undefined)[],
|
||||
): void {
|
||||
this.rev++
|
||||
this.eventIndex = new Map()
|
||||
this.callIdx = new Map()
|
||||
@@ -228,7 +235,7 @@ export class TranscriptAdapter {
|
||||
/* v8 ignore next -- dense-array guard: i stays within events.length, so the undefined arm needs a sparse array no caller builds. */
|
||||
if (event === undefined) continue
|
||||
this.eventIndex.set(event.seq, event)
|
||||
this.indexCall(event, views?.[i])
|
||||
this.indexCall(event, views?.[i], calls?.[i])
|
||||
this.indexCommand(event)
|
||||
if (this.steeringHistory.apply(event)) steeringSeqs.add(event.seq)
|
||||
indexAssistantStepTiming(this.stepTimings, event)
|
||||
@@ -338,9 +345,20 @@ export class TranscriptAdapter {
|
||||
return true
|
||||
}
|
||||
|
||||
private indexCall(event: SessionEvent, view?: ToolEventView): void {
|
||||
private indexCall(event: SessionEvent, view?: ToolEventView, pairedCall?: HistoryToolCall): void {
|
||||
if (event.type === 'tool/result') {
|
||||
if (view?.for === 'result') this.resultViews.set(event.seq, view.view)
|
||||
const callId = String(event.data.message.source.callId)
|
||||
if (!this.callIdx.has(callId) && pairedCall !== undefined) {
|
||||
this.callIdx.set(callId, {
|
||||
name: pairedCall.name,
|
||||
argsRaw: pairedCall.arguments,
|
||||
turn: event.data.turn,
|
||||
step: event.data.step,
|
||||
time: pairedCall.time,
|
||||
callView: null,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
if (event.type !== 'tool/call') return
|
||||
|
||||
Reference in New Issue
Block a user