实现 Codex 风格上下文压缩时间线交互

This commit is contained in:
2026-08-15 08:43:33 +08:00
parent 953b0f491e
commit fd9b5b46a9
13 changed files with 2044 additions and 73 deletions

View File

@@ -1,8 +1,12 @@
import { describe, expect, it } from 'vitest';
import {
completeCompaction,
createPendingCompaction,
getOrderedSessionParts,
getOrderedSessionCompactions,
hydrateOpenCodeSession,
reduceOpenCodeEvent,
removeCompaction,
} from '@/lib/opencode-session-state';
describe('OpenCode native session state', () => {
@@ -171,4 +175,261 @@ describe('OpenCode native session state', () => {
});
expect(getOrderedSessionParts(secondRealChunk, 'msg_1')[0]?.text).toBe('xx');
});
it('hydrates historical compaction Parts as completed timeline events in transcript order', () => {
const state = hydrateOpenCodeSession('ses_1', [{
info: { id: 'msg_1', role: 'assistant', sessionID: 'ses_1' },
parts: [
{ id: 'compact_1', type: 'compaction', auto: false },
{ id: 'compact_2', type: 'compaction', auto: true },
],
}]);
expect(getOrderedSessionCompactions(state)).toEqual([
expect.objectContaining({
id: 'compaction:ses_1:part:compact_1',
source: 'manual',
status: 'completed',
order: 0,
anchorMessageID: 'msg_1',
anchorPartID: 'compact_1',
nativePartID: 'compact_1',
}),
expect.objectContaining({
source: 'automatic',
status: 'completed',
order: 1,
nativePartID: 'compact_2',
}),
]);
});
it('allows only the latest compaction in an active snapshot to remain running', () => {
const state = hydrateOpenCodeSession('ses_1', [{
info: { id: 'msg_1', role: 'assistant', sessionID: 'ses_1' },
parts: [
{ id: 'compact_1', type: 'compaction', auto: true },
{ id: 'compact_2', type: 'compaction', auto: true },
],
}], 'busy');
expect(getOrderedSessionCompactions(state).map((event) => event.status))
.toEqual(['completed', 'running']);
});
it('does not downgrade a completed compaction during busy history hydration', () => {
const snapshot = [{
info: { id: 'msg_1', role: 'assistant', sessionID: 'ses_1' },
parts: [{ id: 'compact_1', type: 'compaction', auto: true }],
}];
const running = hydrateOpenCodeSession('ses_1', snapshot, 'busy');
const completed = reduceOpenCodeEvent(running, {
type: 'session.compacted',
payload: { sessionID: 'ses_1', eventID: 'evt_compacted_1' },
});
const rehydrated = hydrateOpenCodeSession('ses_1', snapshot, 'busy', completed);
expect(getOrderedSessionCompactions(rehydrated)).toEqual([
expect.objectContaining({
nativePartID: 'compact_1',
status: 'completed',
}),
]);
});
it('merges a manual pending event with its native Part without changing the UI id', () => {
const empty = hydrateOpenCodeSession('ses_1', [], 'busy');
const pending = createPendingCompaction(empty, {
id: 'ui_compact_1',
source: 'manual',
runID: 'run_1',
generation: 3,
startedAt: 1_700_000_000_000,
});
const withNativePart = reduceOpenCodeEvent(pending, {
type: 'message.part.updated',
payload: {
sessionID: 'ses_1',
eventID: 'evt_part_1',
messageID: 'msg_1',
part: {
id: 'native_compact_1',
type: 'compaction',
auto: false,
runID: 'run_1',
generation: 3,
},
},
});
expect(getOrderedSessionCompactions(withNativePart)).toEqual([
expect.objectContaining({
id: 'ui_compact_1',
nativePartID: 'native_compact_1',
nativeEventID: 'evt_part_1',
anchorMessageID: 'msg_1',
status: 'running',
}),
]);
const replayed = reduceOpenCodeEvent(withNativePart, {
type: 'message.part.updated',
payload: {
sessionID: 'ses_1',
eventID: 'evt_part_2',
messageID: 'msg_1',
part: { id: 'native_compact_1', type: 'compaction', auto: false },
},
});
expect(getOrderedSessionCompactions(replayed)).toHaveLength(1);
expect(getOrderedSessionCompactions(replayed)[0]?.id).toBe('ui_compact_1');
});
it('preserves unmatched running pending events across active hydration only', () => {
const current = createPendingCompaction(
hydrateOpenCodeSession('ses_1', [], 'busy'),
{ id: 'ui_pending', source: 'manual', runID: 'run_1', generation: 1 },
);
const active = hydrateOpenCodeSession('ses_1', [], 'busy', current);
expect(getOrderedSessionCompactions(active).map((event) => event.id)).toEqual(['ui_pending']);
const historical = hydrateOpenCodeSession('ses_1', [], 'idle', current);
expect(getOrderedSessionCompactions(historical)).toEqual([]);
});
it('merges an active snapshot Part into the current manual pending UI identity', () => {
const current = createPendingCompaction(
hydrateOpenCodeSession('ses_1', [], 'busy'),
{ id: 'ui_pending', source: 'manual', runID: 'run_1', generation: 1 },
);
const hydrated = hydrateOpenCodeSession('ses_1', [{
info: { id: 'msg_1', role: 'assistant', sessionID: 'ses_1' },
parts: [{ id: 'native_compact_1', type: 'compaction', auto: false }],
}], 'busy', current);
expect(getOrderedSessionCompactions(hydrated)).toEqual([
expect.objectContaining({
id: 'ui_pending',
nativePartID: 'native_compact_1',
status: 'running',
runID: 'run_1',
generation: 1,
}),
]);
});
it('closes an older running native compaction when a later one arrives', () => {
let state = hydrateOpenCodeSession('ses_1', [], 'busy');
for (const [messageID, partID] of [['msg_1', 'compact_1'], ['msg_2', 'compact_2']]) {
state = reduceOpenCodeEvent(state, {
type: 'message.part.updated',
payload: {
sessionID: 'ses_1',
messageID,
part: { id: partID, type: 'compaction', auto: true },
},
})!;
}
expect(getOrderedSessionCompactions(state).map((event) => event.status))
.toEqual(['completed', 'running']);
});
it('completes and removes only the compaction matching all supplied correlation keys', () => {
let state = createPendingCompaction(
hydrateOpenCodeSession('ses_1', [], 'busy'),
{ id: 'ui_1', source: 'manual', runID: 'run_1', generation: 1 },
);
state = createPendingCompaction(state, {
id: 'ui_2',
source: 'manual',
runID: 'run_2',
generation: 2,
nativePartID: 'native_2',
});
const wrongGeneration = completeCompaction(
state,
{ runID: 'run_2', generation: 1, nativePartID: 'native_2' },
);
expect(wrongGeneration).toBe(state);
state = completeCompaction(
state,
{ runID: 'run_2', generation: 2, nativePartID: 'native_2' },
{ nativeEventID: 'evt_complete_2', completedAt: 1_700_000_000_100 },
);
expect(state.compactionsById.ui_2).toEqual(expect.objectContaining({
status: 'completed',
nativeEventID: 'evt_complete_2',
completedAt: 1_700_000_000_100,
}));
expect(state.compactionsById.ui_1?.status).toBe('running');
const untouched = removeCompaction(state, { runID: 'run_1', generation: 2 });
expect(untouched).toBe(state);
state = removeCompaction(state, { runID: 'run_1', generation: 1 });
expect(getOrderedSessionCompactions(state).map((event) => event.id)).toEqual(['ui_2']);
});
it('completes compaction on session.compacted without marking the transcript idle', () => {
const busy = createPendingCompaction(
hydrateOpenCodeSession('ses_1', [], 'busy'),
{ id: 'ui_compact_1', source: 'automatic', runID: 'run_1', generation: 4 },
);
const compacted = reduceOpenCodeEvent(busy, {
type: 'session.compacted',
payload: {
sessionID: 'ses_1',
eventID: 'evt_complete_1',
runID: 'run_1',
generation: 4,
},
});
expect(compacted?.status).toBe('busy');
expect(compacted?.compactionsById.ui_compact_1).toEqual(expect.objectContaining({
status: 'completed',
nativeEventID: 'evt_complete_1',
}));
const idle = reduceOpenCodeEvent(compacted, {
type: 'session.idle',
payload: { sessionID: 'ses_1' },
});
expect(idle?.status).toBe('idle');
});
it('uses session.idle as a successful completion fallback', () => {
const busy = createPendingCompaction(
hydrateOpenCodeSession('ses_1', [], 'busy'),
{ id: 'ui_compact_idle', source: 'manual', runID: 'run_idle', generation: 5 },
);
const idle = reduceOpenCodeEvent(busy, {
type: 'session.idle',
payload: { sessionID: 'ses_1' },
});
expect(idle?.status).toBe('idle');
expect(idle?.compactionsById.ui_compact_idle?.status).toBe('completed');
});
it('removes unfinished compactions on a fatal session error', () => {
const busy = createPendingCompaction(
hydrateOpenCodeSession('ses_1', [], 'busy'),
{ id: 'ui_compact_error', source: 'automatic', runID: 'run_error', generation: 6 },
);
const failed = reduceOpenCodeEvent(busy, {
type: 'session.error',
payload: { sessionID: 'ses_1', error: 'provider failed' },
});
expect(failed?.status).toBe('idle');
expect(failed?.compactionOrder).toEqual([]);
expect(failed?.compactionsById.ui_compact_error).toBeUndefined();
});
});