agentxchain 2.155.62 → 2.155.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/lib/turn-checkpoint.js +91 -7
package/package.json
CHANGED
|
@@ -96,7 +96,69 @@ function recoverLegacyCheckpointFiles(root, entry, opts = {}) {
|
|
|
96
96
|
return getActorDirtyFiles(root, opts.dirtyFiles);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
function
|
|
99
|
+
function extractObservedDiffSummaryPaths(entry) {
|
|
100
|
+
const summary = entry?.observed_artifact?.diff_summary;
|
|
101
|
+
if (typeof summary !== 'string' || summary.trim().length === 0) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const paths = [];
|
|
106
|
+
for (const line of summary.split('\n')) {
|
|
107
|
+
const tableMatch = line.match(/^\s*(.+?)\s+\|\s+/);
|
|
108
|
+
if (tableMatch?.[1]) {
|
|
109
|
+
paths.push(tableMatch[1].trim());
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const untrackedMatch = line.match(/^\s*-\s+(.+)$/);
|
|
114
|
+
if (untrackedMatch?.[1]) {
|
|
115
|
+
paths.push(untrackedMatch[1].trim());
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return normalizeFilesChanged(paths);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function recoverSupplementalCheckpointFiles(root, entry, opts = {}) {
|
|
123
|
+
if (!supportsLegacyFilesChangedRecovery(entry) || !entry?.checkpoint_sha) {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const state = readState(root);
|
|
128
|
+
if (state && Object.keys(state.active_turns || {}).length > 0) {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const acceptedHistory = queryAcceptedTurnHistory(root);
|
|
133
|
+
if (acceptedHistory[0]?.turn_id !== entry?.turn_id) {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const actorDirtyFiles = getActorDirtyFiles(root, opts.dirtyFiles);
|
|
138
|
+
if (actorDirtyFiles.length === 0) {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const observedSummaryFiles = new Set(extractObservedDiffSummaryPaths(entry));
|
|
143
|
+
if (observedSummaryFiles.size === 0) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const declared = new Set(normalizeFilesChanged(entry.files_changed));
|
|
148
|
+
const recoverable = actorDirtyFiles.filter((filePath) => (
|
|
149
|
+
!declared.has(filePath) && observedSummaryFiles.has(filePath)
|
|
150
|
+
));
|
|
151
|
+
if (recoverable.length === 0) {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const unrecoverable = actorDirtyFiles.filter((filePath) => (
|
|
156
|
+
!declared.has(filePath) && !observedSummaryFiles.has(filePath)
|
|
157
|
+
));
|
|
158
|
+
return unrecoverable.length === 0 ? recoverable : [];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function persistRecoveredFilesChanged(root, turnId, recoveredFiles, source = 'legacy_dirty_worktree') {
|
|
100
162
|
const normalizedRecoveredFiles = normalizeFilesChanged(recoveredFiles);
|
|
101
163
|
if (normalizedRecoveredFiles.length === 0) return;
|
|
102
164
|
|
|
@@ -110,15 +172,21 @@ function persistRecoveredFilesChanged(root, turnId, recoveredFiles) {
|
|
|
110
172
|
? historyEntry.observed_artifact
|
|
111
173
|
: null;
|
|
112
174
|
|
|
175
|
+
const existingFiles = normalizeFilesChanged(historyEntry.files_changed);
|
|
176
|
+
const mergedFiles = normalizeFilesChanged([...existingFiles, ...normalizedRecoveredFiles]);
|
|
177
|
+
|
|
113
178
|
return {
|
|
114
179
|
...historyEntry,
|
|
115
|
-
files_changed:
|
|
180
|
+
files_changed: mergedFiles,
|
|
116
181
|
files_changed_recovered_at: recoveredAt,
|
|
117
|
-
files_changed_recovery_source:
|
|
182
|
+
files_changed_recovery_source: source,
|
|
118
183
|
observed_artifact: observedArtifact
|
|
119
184
|
? {
|
|
120
185
|
...observedArtifact,
|
|
121
|
-
files_changed:
|
|
186
|
+
files_changed: normalizeFilesChanged([
|
|
187
|
+
...(Array.isArray(observedArtifact.files_changed) ? observedArtifact.files_changed : []),
|
|
188
|
+
...normalizedRecoveredFiles,
|
|
189
|
+
]),
|
|
122
190
|
}
|
|
123
191
|
: observedArtifact,
|
|
124
192
|
};
|
|
@@ -234,7 +302,17 @@ export function detectPendingCheckpoint(root, dirtyFiles = []) {
|
|
|
234
302
|
if (!resolved.ok || !resolved.entry) return { required: false };
|
|
235
303
|
|
|
236
304
|
const entry = resolved.entry;
|
|
237
|
-
if (entry.checkpoint_sha)
|
|
305
|
+
if (entry.checkpoint_sha) {
|
|
306
|
+
const supplementalFiles = recoverSupplementalCheckpointFiles(root, entry, { dirtyFiles: actorDirtyFiles });
|
|
307
|
+
if (supplementalFiles.length === 0) return { required: false };
|
|
308
|
+
return {
|
|
309
|
+
required: true,
|
|
310
|
+
turn_id: entry.turn_id,
|
|
311
|
+
recovered_files_changed: supplementalFiles,
|
|
312
|
+
supplemental: true,
|
|
313
|
+
message: `Accepted turn ${entry.turn_id} is checkpointed but still owns ${supplementalFiles.length} dirty actor file(s) from its observed diff summary. Run agentxchain checkpoint-turn --turn ${entry.turn_id} to create a supplemental checkpoint before assigning the next code-writing turn.`,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
238
316
|
|
|
239
317
|
const turnFiles = normalizeFilesChanged(entry.files_changed);
|
|
240
318
|
const recoveredFiles = turnFiles.length === 0
|
|
@@ -267,7 +345,10 @@ export function checkpointAcceptedTurn(root, opts = {}) {
|
|
|
267
345
|
}
|
|
268
346
|
|
|
269
347
|
const entry = resolved.entry;
|
|
270
|
-
|
|
348
|
+
const supplementalFilesChanged = entry.checkpoint_sha
|
|
349
|
+
? recoverSupplementalCheckpointFiles(root, entry)
|
|
350
|
+
: [];
|
|
351
|
+
if (entry.checkpoint_sha && supplementalFilesChanged.length === 0) {
|
|
271
352
|
return {
|
|
272
353
|
ok: true,
|
|
273
354
|
already_checkpointed: true,
|
|
@@ -281,11 +362,14 @@ export function checkpointAcceptedTurn(root, opts = {}) {
|
|
|
281
362
|
? recoverLegacyCheckpointFiles(root, entry)
|
|
282
363
|
: [];
|
|
283
364
|
const filesChanged = declaredFilesChanged.length > 0
|
|
284
|
-
? declaredFilesChanged
|
|
365
|
+
? normalizeFilesChanged([...declaredFilesChanged, ...supplementalFilesChanged])
|
|
285
366
|
: recoveredFilesChanged;
|
|
286
367
|
if (recoveredFilesChanged.length > 0) {
|
|
287
368
|
persistRecoveredFilesChanged(root, entry.turn_id, recoveredFilesChanged);
|
|
288
369
|
}
|
|
370
|
+
if (supplementalFilesChanged.length > 0) {
|
|
371
|
+
persistRecoveredFilesChanged(root, entry.turn_id, supplementalFilesChanged, 'supplemental_dirty_worktree');
|
|
372
|
+
}
|
|
289
373
|
|
|
290
374
|
if (filesChanged.length === 0) {
|
|
291
375
|
return {
|