agentxchain 2.155.61 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "2.155.61",
3
+ "version": "2.155.63",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2204,10 +2204,6 @@ export async function executeContinuousRun(context, contOpts, executeGovernedRun
2204
2204
  while (!stopping) {
2205
2205
  const step = await advanceContinuousRunOnce(context, session, contOpts, executeGovernedRun, log);
2206
2206
 
2207
- if (recoverPausedActiveContinuousSession(context, session, log, `post_step_${step.action || step.status || 'unknown'}`)) {
2208
- continue;
2209
- }
2210
-
2211
2207
  // Terminal states
2212
2208
  if (step.status === 'completed' || step.status === 'idle_exit' || step.status === 'failed' || step.status === 'blocked' || step.status === 'stopped' || step.status === 'vision_exhausted' || step.status === 'vision_expansion_exhausted' || step.status === 'session_budget') {
2213
2209
  const terminalMessage = describeContinuousTerminalStep(step, contOpts);
@@ -2217,6 +2213,10 @@ export async function executeContinuousRun(context, contOpts, executeGovernedRun
2217
2213
  return { exitCode: step.ok ? 0 : 1, session };
2218
2214
  }
2219
2215
 
2216
+ if (recoverPausedActiveContinuousSession(context, session, log, `post_step_${step.action || step.status || 'unknown'}`)) {
2217
+ continue;
2218
+ }
2219
+
2220
2220
  // Non-terminal: sleep before next step
2221
2221
  if (!stopping) {
2222
2222
  const sleepMs = step.action === 'no_work_found' || step.action === 'waited_for_human'
@@ -96,7 +96,69 @@ function recoverLegacyCheckpointFiles(root, entry, opts = {}) {
96
96
  return getActorDirtyFiles(root, opts.dirtyFiles);
97
97
  }
98
98
 
99
- function persistRecoveredFilesChanged(root, turnId, recoveredFiles) {
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: normalizedRecoveredFiles,
180
+ files_changed: mergedFiles,
116
181
  files_changed_recovered_at: recoveredAt,
117
- files_changed_recovery_source: 'legacy_dirty_worktree',
182
+ files_changed_recovery_source: source,
118
183
  observed_artifact: observedArtifact
119
184
  ? {
120
185
  ...observedArtifact,
121
- files_changed: normalizedRecoveredFiles,
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) return { required: false };
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
- if (entry.checkpoint_sha) {
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 {