Просмотр исходного кода

fix(session): avoid retaining surface fold history

Hypatia May 2 месяцев назад
Родитель
Сommit
42d85cf76f
2 измененных файлов с 27 добавлено и 13 удалено
  1. 14 13
      packages/core/session/src/surface.ts
  2. 13 0
      packages/core/session/tests/surface.spec.ts

+ 14 - 13
packages/core/session/src/surface.ts

@@ -85,7 +85,6 @@ export interface SurfaceFoldResult {
 interface SurfaceFoldState {
   nodes: SurfaceNode[]
   nodeBySeq: Map<number, SurfaceNode>
-  replacements: SurfaceFoldReplacement[]
   replaceGeneration: number
 }
 
@@ -94,13 +93,15 @@ function createFoldState(replaceGeneration = 0): SurfaceFoldState {
   return {
     nodes: [],
     nodeBySeq: new Map(),
-    replacements: [],
     replaceGeneration,
   }
 }
 
-/** Apply one event to a surface fold state. */
-function applySurfaceEvent(state: SurfaceFoldState, event: SessionEvent): void {
+/** Apply one event and return replacement metadata only when one occurred. */
+function applySurfaceEvent(
+  state: SurfaceFoldState,
+  event: SessionEvent,
+): SurfaceFoldReplacement | undefined {
   if (!isSurfaceEvent(event)) return
 
   if (event.surfaceOp === 'append') {
@@ -112,13 +113,12 @@ function applySurfaceEvent(state: SurfaceFoldState, event: SessionEvent): void {
     return
   }
 
-  const shadowedSeqs = replaceSurface(state, event.seq, event.surfaceOp)
-  state.replacements.push({
+  return {
     seq: event.seq,
     start: event.surfaceOp.start,
     end: event.surfaceOp.end,
-    shadowedSeqs,
-  })
+    shadowedSeqs: replaceSurface(state, event.seq, event.surfaceOp),
+  }
 }
 
 /** Apply one positional replacement and return the nodes it removed. */
@@ -170,13 +170,14 @@ function replaceSurface(
  */
 export function foldSurface(events: readonly SessionEvent[]): SurfaceFoldResult {
   const state = createFoldState()
-  for (const event of events) applySurfaceEvent(state, event)
+  const replacements: SurfaceFoldReplacement[] = []
+  for (const event of events) {
+    const replacement = applySurfaceEvent(state, event)
+    if (replacement !== undefined) replacements.push(replacement)
+  }
   return {
     nodes: state.nodes.map(node => ({ ...node })),
-    replacements: state.replacements.map(replacement => ({
-      ...replacement,
-      shadowedSeqs: [...replacement.shadowedSeqs],
-    })),
+    replacements,
   }
 }
 

+ 13 - 0
packages/core/session/tests/surface.spec.ts

@@ -33,6 +33,19 @@ describe('SurfaceManager', () => {
     expect(foldSurface(s.events).replacements[0]!.shadowedSeqs).toEqual([0])
   })
 
+  it('does not retain fold-only replacement history in incremental state', () => {
+    const s = new Session(SessionId('incremental-state'))
+    s.append('user/message', { content: [{ type: 'text', text: 'a' }], source: { kind: 'user' } }, { surfaceOp: 'append' })
+    s.append('assistant/message', { turn: 1, step: 1, content: [{ type: 'text', text: 'b' }] }, { surfaceOp: { op: 'replace', start: 0, end: 0 } })
+
+    expect(s.surface.nodes).toEqual([{ seq: 1, prev: null, next: null }])
+    const manager = s.surface as unknown as { _state: object }
+    expect(Object.hasOwn(manager._state, 'replacements')).toBe(false)
+    expect(foldSurface(s.events).replacements).toEqual([
+      { seq: 1, start: 0, end: 0, shadowedSeqs: [0] },
+    ])
+  })
+
   it('foldSurface reports the same invalid replacement failures as the incremental manager', () => {
     const s = new Session(SessionId('shared-fold-invalid'))
     s.append('user/message', { content: [{ type: 'text', text: 'a' }], source: { kind: 'user' } }, { surfaceOp: 'append' })