-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy pathrevertFileTool.ts
More file actions
198 lines (174 loc) · 7.99 KB
/
revertFileTool.ts
File metadata and controls
198 lines (174 loc) · 7.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/**
* Copyright 2026 Reto Meier
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { MultiAgentTool } from '../multiAgentTool.js';
import { MultiAgentToolContext, MultiAgentToolResult, ToolParsingResult } from '../../momoa_core/types.js';
import { fileNameLookup } from '../../utils/fileNameLookup.js';
import { getCombinedFileMap } from '../../utils/fileMapUtils.js';
import { generateDiffString, isLockFile, getLockFileHiddenPlaceholder } from '../../utils/diffGenerator.js';
import { getAssetString } from '../../services/promptManager.js';
import { updateFileEntry } from '../../utils/fileAnalysis.js';
/**
* Regenerates the project diff and updates the diff block in all
* transcripts that are registered in the context.
*/
export async function updateDiffInAllTranscripts(context: MultiAgentToolContext) {
const docId = PROJECT_DIFF_ID;
if (!docId || !context.transcriptsToUpdate) {
return;
}
const newDiffBlock = generateDiffString(context);
context.transcriptsToUpdate.forEach(transcript => {
transcript.replaceEntry(docId, newDiffBlock);
});
console.log(`Updated diff block '${docId}' in all registered transcripts.`);
}
export const PROJECT_DIFF_ID = "PROJECT_DIFF_ID";
export const revertFileTool: MultiAgentTool = {
displayName: "Revert File",
name: 'DOC/REVERT{',
endToken: '}',
async execute(params: Record<string, string>, context: MultiAgentToolContext): Promise<MultiAgentToolResult> {
const providedFilename = params.filename;
if (!providedFilename) {
return { result: "Error: 'filename' parameter is missing." };
}
if (!context.originalFileMap || !context.originalBinaryFileMap) {
return { result: "Error: Orchestrator did not provide original file maps to context." };
}
// Use fileNameLookup to find the precise file name
const allFilesMap = getCombinedFileMap(context.fileMap, context.binaryFileMap);
const filename = await fileNameLookup(providedFilename, allFilesMap, context.multiAgentGeminiClient);
const originalExisted = context.originalFileMap.has(filename) || context.originalBinaryFileMap.has(filename);
const currentExisted = context.fileMap.has(filename) || context.binaryFileMap.has(filename);
let revertMessage = `---Error: Could not revert '${filename}'. It was not found in the current project state.---`;
let revertSucceeded = false;
let finalContent: string | null | undefined = undefined;
let isBinary = false;
// Case 1: Revert text file (it existed originally)
if (context.originalFileMap.has(filename)) {
const originalContent = context.originalFileMap.get(filename)!;
context.fileMap.set(filename, originalContent);
context.binaryFileMap.delete(filename); // Clean up just in case
// We will remove from editedFilesSet *after* updating transcripts
revertMessage = `---File '${filename}' has been successfully reverted to its original state.---`;
revertSucceeded = true;
finalContent = originalContent;
isBinary = false;
}
// Case 2: Revert binary file (it existed originally)
else if (context.originalBinaryFileMap.has(filename)) {
const originalContent = context.originalBinaryFileMap.get(filename)!;
context.binaryFileMap.set(filename, originalContent);
context.fileMap.delete(filename); // Clean up just in case
// We will remove from editedFilesSet *after* updating transcripts
revertMessage = `---File '${filename}' has been successfully reverted to its original state.---`;
revertSucceeded = true;
finalContent = null;
isBinary = true;
}
// Case 3: File was created (didn't exist originally), so "reverting" means deleting it.
else if (!originalExisted && currentExisted) {
context.fileMap.delete(filename);
context.binaryFileMap.delete(filename);
// We will remove from editedFilesSet *after* updating transcripts
revertMessage = `---File '${filename}' was created during this run and has been successfully deleted (reverted).---`;
revertSucceeded = true;
finalContent = undefined; // Flag for deleted
isBinary = false;
}
// Case 4: File doesn't exist now and didn't exist originally
else if (!originalExisted && !currentExisted) {
revertMessage = `---Error: Could not revert '${filename}'. It was not found in the current project or the original project state.---`;
}
// If a revert or deletion succeeded, update all transcripts
if (revertSucceeded) {
context.sendMessage({
type: 'PROGRESS_UPDATE',
message: `Undoing changes to \`${filename}\`, and reverting it to its original content.`,
});
// If the file was not deleted, force a re-analysis
// to update its description and remove any "---DELETED---" markers.
if (finalContent !== undefined) { // undefined means it was deleted
try {
if (!context.binaryFileMap.has(filename)) {
await updateFileEntry(filename, context.fileMap, context.multiAgentGeminiClient);
} else {
await updateFileEntry(filename, context.fileMap, undefined,
{
filename: filename,
description: "[Binary File]"
},
true);
}
} catch (e: any) {
console.warn(`Failed to re-analyze reverted file ${filename}: ${e.message}`);
}
}
// Immediately supersede this file's entry in all transcripts
// (WorkPhase experts + Orchestrator) *before* removing from the set.
context.transcriptsToUpdate?.forEach(transcript => {
transcript.supersedeEntry(filename);
});
// Now that transcripts are updated, remove it from the set.
context.editedFilesSet.delete(filename);
}
// Always update the diff block in all transcripts
await updateDiffInAllTranscripts(context);
// --- Format Return Value ---
const prefix = await getAssetString('file-content-prefix');
const suffix = await getAssetString('file-content-suffix');
const replacementString = await getAssetString('file-content-removed');
if (!revertSucceeded) {
// Return the error message, but still provide the ID/replacement
// so the WorkPhase supersedes any *previous* read of this file.
return {
result: revertMessage,
transcriptReplacementID: filename,
transcriptReplacementString: `${prefix}\n${replacementString}\n${suffix}`
};
}
// Build the final result string
let finalResultString = revertMessage;
if (finalContent !== undefined) { // File exists post-revert
if (isBinary) {
finalResultString += `\nFile '${filename}' is a binary file.`;
} else if (isLockFile(filename)) {
finalResultString += `\n${getLockFileHiddenPlaceholder(filename)}`;
} else if (finalContent !== null) {
finalResultString += `\n${prefix}\n${finalContent}\n${suffix}`;
}
}
return {
result: finalResultString,
transcriptReplacementID: filename,
transcriptReplacementString: `${prefix}\n${replacementString}\n${suffix}`
};
},
async extractParameters(invocation: string, _context: MultiAgentToolContext): Promise<ToolParsingResult> {
if (invocation.trim().endsWith("}")) {
const filename = invocation.trim().slice(0, -1).trim();
return {
success: true,
params: { filename }
};
} else {
return {
success: false,
error: `Invalid syntax for ${this.displayName}. Expected DOC/REVERT{filename.txt}`
};
}
}
};