fix(session): avoid retaining surface fold history
This commit is contained in:
@@ -85,7 +85,6 @@ export interface SurfaceFoldResult {
|
|||||||
interface SurfaceFoldState {
|
interface SurfaceFoldState {
|
||||||
nodes: SurfaceNode[]
|
nodes: SurfaceNode[]
|
||||||
nodeBySeq: Map<number, SurfaceNode>
|
nodeBySeq: Map<number, SurfaceNode>
|
||||||
replacements: SurfaceFoldReplacement[]
|
|
||||||
replaceGeneration: number
|
replaceGeneration: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,13 +93,15 @@ function createFoldState(replaceGeneration = 0): SurfaceFoldState {
|
|||||||
return {
|
return {
|
||||||
nodes: [],
|
nodes: [],
|
||||||
nodeBySeq: new Map(),
|
nodeBySeq: new Map(),
|
||||||
replacements: [],
|
|
||||||
replaceGeneration,
|
replaceGeneration,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Apply one event to a surface fold state. */
|
/** Apply one event and return replacement metadata only when one occurred. */
|
||||||
function applySurfaceEvent(state: SurfaceFoldState, event: SessionEvent): void {
|
function applySurfaceEvent(
|
||||||
|
state: SurfaceFoldState,
|
||||||
|
event: SessionEvent,
|
||||||
|
): SurfaceFoldReplacement | undefined {
|
||||||
if (!isSurfaceEvent(event)) return
|
if (!isSurfaceEvent(event)) return
|
||||||
|
|
||||||
if (event.surfaceOp === 'append') {
|
if (event.surfaceOp === 'append') {
|
||||||
@@ -112,13 +113,12 @@ function applySurfaceEvent(state: SurfaceFoldState, event: SessionEvent): void {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const shadowedSeqs = replaceSurface(state, event.seq, event.surfaceOp)
|
return {
|
||||||
state.replacements.push({
|
|
||||||
seq: event.seq,
|
seq: event.seq,
|
||||||
start: event.surfaceOp.start,
|
start: event.surfaceOp.start,
|
||||||
end: event.surfaceOp.end,
|
end: event.surfaceOp.end,
|
||||||
shadowedSeqs,
|
shadowedSeqs: replaceSurface(state, event.seq, event.surfaceOp),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Apply one positional replacement and return the nodes it removed. */
|
/** Apply one positional replacement and return the nodes it removed. */
|
||||||
@@ -170,13 +170,14 @@ function replaceSurface(
|
|||||||
*/
|
*/
|
||||||
export function foldSurface(events: readonly SessionEvent[]): SurfaceFoldResult {
|
export function foldSurface(events: readonly SessionEvent[]): SurfaceFoldResult {
|
||||||
const state = createFoldState()
|
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 {
|
return {
|
||||||
nodes: state.nodes.map(node => ({ ...node })),
|
nodes: state.nodes.map(node => ({ ...node })),
|
||||||
replacements: state.replacements.map(replacement => ({
|
replacements,
|
||||||
...replacement,
|
|
||||||
shadowedSeqs: [...replacement.shadowedSeqs],
|
|
||||||
})),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,19 @@ describe('SurfaceManager', () => {
|
|||||||
expect(foldSurface(s.events).replacements[0]!.shadowedSeqs).toEqual([0])
|
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', () => {
|
it('foldSurface reports the same invalid replacement failures as the incremental manager', () => {
|
||||||
const s = new Session(SessionId('shared-fold-invalid'))
|
const s = new Session(SessionId('shared-fold-invalid'))
|
||||||
s.append('user/message', { content: [{ type: 'text', text: 'a' }], source: { kind: 'user' } }, { surfaceOp: 'append' })
|
s.append('user/message', { content: [{ type: 'text', text: 'a' }], source: { kind: 'user' } }, { surfaceOp: 'append' })
|
||||||
|
|||||||
Reference in New Issue
Block a user