Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
54 changes: 54 additions & 0 deletions packages/acp-bridge/src/transcript-replay.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,60 @@ describe('createTranscriptReplayMachine', () => {
expect(machine.snapshot().pendingToolCalls).toEqual([]);
});

it('prefers filePath over the fileName basename when replaying an edit diff', () => {
const machine = createTranscriptReplayMachine();
updates(
machine,
record('assistant-1', 'assistant', {
message: {
role: 'model',
parts: [
{ functionCall: { name: 'edit_file', args: {}, id: 'call-1' } },
],
},
}),
);
const result = updates(
machine,
record('result-1', 'tool_result', {
message: {
role: 'user',
parts: [
{
functionResponse: {
name: 'edit_file',
response: { output: 'edited' },
},
},
],
},
toolCallResult: {
callId: 'call-1',
resultDisplay: {
fileDiff: '--- a\n+++ b\n',
fileName: 'Foo.kt',
filePath: '/workspace/app/src/main/java/com/example/Foo.kt',
originalContent: 'old',
newContent: 'new',
},
},
}),
);

expect(result[0]).toMatchObject({
sessionUpdate: 'tool_call_update',
toolCallId: 'call-1',
content: [
{
type: 'diff',
path: '/workspace/app/src/main/java/com/example/Foo.kt',
oldText: 'old',
newText: 'new',
},
],
});
});

it('reports ambiguous same-name result correlation', () => {
const onDiagnostic = vi.fn();
const machine = createTranscriptReplayMachine({ onDiagnostic });
Expand Down
8 changes: 5 additions & 3 deletions packages/acp-bridge/src/transcript-replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1304,9 +1304,11 @@ function extractDiffContent(resultDisplay: unknown): ToolCallContent | null {
return {
type: 'diff',
path:
typeof resultDisplay['fileName'] === 'string'
? resultDisplay['fileName']
: '',
typeof resultDisplay['filePath'] === 'string'
? resultDisplay['filePath']
: typeof resultDisplay['fileName'] === 'string'
? resultDisplay['fileName']
: '',
oldText:
typeof resultDisplay['originalContent'] === 'string'
? resultDisplay['originalContent']
Expand Down
53 changes: 53 additions & 0 deletions packages/cli/src/ui/utils/export/normalize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,59 @@ describe('normalizeSessionData', () => {
]);
});

it('exports the diff path from filePath rather than the fileName basename', () => {
const record: ChatRecord = {
uuid: 'tool-2',
parentUuid: null,
sessionId: 'session-1',
timestamp: '2025-01-01T00:00:00.000Z',
type: 'tool_result',
cwd: '',
version: '1.0.0',
message: {
role: 'user',
parts: [
{
functionResponse: {
id: 'call-2',
name: 'edit_file',
response: { output: 'ok' },
},
},
],
},
toolCallResult: {
callId: 'call-2',
resultDisplay: {
fileName: 'Foo.kt',
filePath: '/workspace/app/src/main/java/com/example/Foo.kt',
fileDiff: '--- Foo.kt\n+++ Foo.kt\n',
originalContent: 'old',
newContent: 'new',
},
},
};

const normalized = normalizeSessionData(
{
sessionId: 'session-1',
startTime: '2025-01-01T00:00:00.000Z',
messages: [],
},
[record],
config,
);

expect(normalized.messages[0].toolCall?.content).toEqual([
{
type: 'diff',
path: '/workspace/app/src/main/java/com/example/Foo.kt',
oldText: 'old',
newText: 'new',
},
]);
});

it('accepts the minimal daemon export config shape', () => {
const minimalConfig: ExportConfig = {};
const record: ChatRecord = {
Expand Down
5 changes: 4 additions & 1 deletion packages/cli/src/ui/utils/export/normalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,10 @@ function extractDiffContent(
return [
{
type: 'diff',
path: display['fileName'] as string,
path:
typeof display['filePath'] === 'string'
? display['filePath']
: (display['fileName'] as string),
oldText: (display['originalContent'] as string) ?? '',
newText: display['newContent'] as string,
},
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/tools/edit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,10 @@ describe('EditTool', () => {
expect(display.fileDiff).toMatch(initialContent);
expect(display.fileDiff).toMatch(newContent);
expect(display.fileName).toBe(testFile);
// `filePath` must carry the full path: UI consumers (e.g. the VSCode
// companion) cannot resolve a clickable location from `fileName`
// alone once the file is outside the workspace root.
expect(display.filePath).toBe(filePath);
});

// trackEdit is best-effort: a FileHistoryService failure (disk full,
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/tools/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import type {
FileDiff,
ToolCallConfirmationDetails,
ToolEditConfirmationDetails,
ToolInvocation,
Expand Down Expand Up @@ -672,9 +673,10 @@ class EditToolInvocation implements ToolInvocation<EditToolParams, ToolResult> {
'Current',
'Proposed',
);
const displayResult = {
const displayResult: FileDiff = {
fileDiff,
fileName,
filePath: this.params.file_path,
originalContent: editData.currentContent,
newContent: editData.newContent,
diffStat,
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/tools/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,13 @@ export interface TaskListResultDisplay {
export interface FileDiff {
fileDiff: string;
fileName: string;
/**
* Full (project-relative or absolute) path to the edited file, as passed
* to the tool. UI consumers must prefer this over `fileName` when
* resolving a clickable/openable location — `fileName` is a basename and
* cannot be used to locate files outside the workspace root.
*/
filePath?: string;
originalContent: string | null;
newContent: string;
diffStat?: DiffStat;
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/tools/write-file.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ describe('WriteFileTool', () => {
expect(writtenContent).toBe(proposedContent);
const display = result.returnDisplay as FileDiff;
expect(display.fileName).toBe('execute_new_file.txt');
expect(display.filePath).toBe(filePath);
expect(display.fileDiff).toMatch(/--- execute_new_file.txt\tOriginal/);
expect(display.fileDiff).toMatch(/\+\+\+ execute_new_file.txt\tWritten/);
expect(display.fileDiff).toMatch(
Expand Down Expand Up @@ -824,6 +825,7 @@ describe('WriteFileTool', () => {
expect(writtenContent).toBe(proposedContent);
const display = result.returnDisplay as FileDiff;
expect(display.fileName).toBe('execute_existing_file.txt');
expect(display.filePath).toBe(filePath);
expect(display.fileDiff).toMatch(
initialContent.replace(/[.*+?^${}()|[\\]\\]/g, '\\$&'),
);
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/tools/write-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ class WriteFileToolInvocation extends BaseToolInvocation<
const displayResult: FileDiff = {
fileDiff,
fileName,
filePath: file_path,
originalContent,
newContent: content,
diffStat,
Expand Down