Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/cli/src/acp-integration/session/Session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3137,6 +3137,23 @@ describe('Session', () => {
expect(session.getRewindableUserTurnCount()).toBe(1);
});

it('counts cleared media placeholders as rewindable prompts (twin divergence)', () => {
// The TUI twin (isUserTextContent in ui/utils/historyMapping.ts)
// excludes microcompaction media-clear placeholders from its rewind
// prompt count. The ACP twin must keep counting them: ACP rewind
// maps against per-prompt file-history snapshots, which ARE created
// for media-only prompts.
const history: Content[] = [
{
role: 'user',
parts: [{ text: '[Old inline media cleared: image/png]' }],
},
];
vi.mocked(mockChat.getHistoryShallow).mockReturnValue(history);

expect(session.getRewindableUserTurnCount()).toBe(1);
});

it('rejects unreachable user turns', () => {
const history: Content[] = [{ role: 'user', parts: [{ text: 'first' }] }];
vi.mocked(mockChat.getHistory).mockReturnValue(history);
Expand Down
7 changes: 7 additions & 0 deletions packages/cli/src/acp-integration/session/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3579,6 +3579,13 @@ export class Session implements SessionContext {
return false;
}

// Deliberate twin divergence: the TUI twin (isUserTextContent in
// packages/cli/src/ui/utils/historyMapping.ts) excludes microcompaction
// media-clear placeholders ('[Old inline media cleared: ...]') from the
// rewind prompt count because a cleared media-only entry never produced
// a TUI user turn. Here the placeholders MUST stay counted: ACP rewind
// maps against per-prompt file-history snapshots, which ARE created for
// media-only prompts. Do not mirror that exclusion into this twin.
return content.parts.some((part) => 'text' in part && part.text);
}

Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/commands/compressCommand.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ describe('compressCommand', () => {
compressionStatus: CompressionStatus.COMPRESSED,
originalTokenCount: 200,
newTokenCount: 100,
compressionKind: 'summarize',
},
},
expect.any(Number),
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/commands/compressCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export const compressCommand: SlashCommand = {
originalTokenCount: compressed.originalTokenCount,
newTokenCount: compressed.newTokenCount,
compressionStatus: compressed.compressionStatus,
compressionKind: 'summarize',
},
} as HistoryItemCompression,
Date.now(),
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/commands/compressFastCommand.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ describe('compressFastCommand', () => {
originalTokenCount: 200,
newTokenCount: 100,
compressionStatus: CompressionStatus.COMPRESSED,
compressionKind: 'fast',
},
},
expect.any(Number),
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/commands/compressFastCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export const compressFastCommand: SlashCommand = {
originalTokenCount: compressed.originalTokenCount,
newTokenCount: compressed.newTokenCount,
compressionStatus: compressed.compressionStatus,
compressionKind: 'fast',
},
} as HistoryItemCompression,
Date.now(),
Expand Down
8 changes: 8 additions & 0 deletions packages/cli/src/ui/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ export interface CompressionProps {
originalTokenCount: number | null;
newTokenCount: number | null;
compressionStatus: CompressionStatus | null;
/**
* Which compression path produced this item. 'summarize' replaces the
* pre-marker history with a synthetic summary prefix; 'fast' (rule-based,
* no LLM summary) removes no user prompts from the API history, so its
* marker must not be treated as a rewind boundary. Absent on items from
* older sessions, which are treated as 'summarize'.
Comment thread
yiliang114 marked this conversation as resolved.
*/
compressionKind?: 'summarize' | 'fast';
}

export interface SummaryProps {
Expand Down
Loading
Loading