agentxchain 2.143.0 → 2.144.0
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
package/scripts/release-bump.sh
CHANGED
|
@@ -79,6 +79,7 @@ ALLOWED_RELEASE_PATHS=(
|
|
|
79
79
|
".planning/LAUNCH_EVIDENCE_REPORT.md"
|
|
80
80
|
".planning/SHOW_HN_DRAFT.md"
|
|
81
81
|
".planning/MARKETING/TWITTER_THREAD.md"
|
|
82
|
+
".planning/MARKETING/LINKEDIN_POST.md"
|
|
82
83
|
".planning/MARKETING/REDDIT_POSTS.md"
|
|
83
84
|
".planning/MARKETING/HN_SUBMISSION.md"
|
|
84
85
|
"website-v2/static/llms.txt"
|
|
@@ -279,6 +279,12 @@ export const RELEASE_ALIGNMENT_SURFACES = [
|
|
|
279
279
|
scopes: [RELEASE_ALIGNMENT_SCOPES.PREBUMP, RELEASE_ALIGNMENT_SCOPES.CURRENT],
|
|
280
280
|
check: validateTextIncludesVersionAndEvidence('.planning/MARKETING/TWITTER_THREAD.md', 'twitter thread draft').check,
|
|
281
281
|
},
|
|
282
|
+
{
|
|
283
|
+
id: 'linkedin_post',
|
|
284
|
+
label: 'linkedin release post draft',
|
|
285
|
+
scopes: [RELEASE_ALIGNMENT_SCOPES.PREBUMP, RELEASE_ALIGNMENT_SCOPES.CURRENT],
|
|
286
|
+
check: validateTextIncludesVersionAndEvidence('.planning/MARKETING/LINKEDIN_POST.md', 'linkedin release post draft').check,
|
|
287
|
+
},
|
|
282
288
|
{
|
|
283
289
|
id: 'reddit_posts',
|
|
284
290
|
label: 'reddit posts draft',
|
package/src/lib/repo-observer.js
CHANGED
|
@@ -31,6 +31,7 @@ export const OPERATIONAL_PATH_PREFIXES = Object.freeze([
|
|
|
31
31
|
'.agentxchain/transactions/',
|
|
32
32
|
'.agentxchain/missions/',
|
|
33
33
|
'.agentxchain/multirepo/',
|
|
34
|
+
'.agentxchain/plugins/',
|
|
34
35
|
'.agentxchain/prompts/',
|
|
35
36
|
]);
|
|
36
37
|
|
|
@@ -84,18 +85,56 @@ export const RUN_CONTINUITY_DIRECTORY_ROOTS = Object.freeze([
|
|
|
84
85
|
...BASELINE_EXEMPT_PATH_PREFIXES.map((prefix) => prefix.replace(/\/$/, '')),
|
|
85
86
|
]);
|
|
86
87
|
|
|
88
|
+
function pathMatchesAnyPrefix(filePath, prefixes) {
|
|
89
|
+
return prefixes.some((prefix) => filePath.startsWith(prefix));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function pathMatchesAnyRoot(filePath, roots) {
|
|
93
|
+
return roots.some((root) => filePath === root || filePath.startsWith(`${root}/`));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Return the repo-owned vs framework-owned classification flags for a path.
|
|
98
|
+
*
|
|
99
|
+
* The categories intentionally overlap:
|
|
100
|
+
* - continuity state is always baseline-exempt
|
|
101
|
+
* - operational paths are always baseline-exempt
|
|
102
|
+
* - some baseline-exempt evidence paths also participate in continuity export
|
|
103
|
+
*
|
|
104
|
+
* This overlap is the contract. The framework does NOT use mutually exclusive
|
|
105
|
+
* buckets for observation, clean-baseline checks, and continuity export.
|
|
106
|
+
*/
|
|
107
|
+
export function classifyRepoPath(filePath) {
|
|
108
|
+
const operational = pathMatchesAnyPrefix(filePath, OPERATIONAL_PATH_PREFIXES)
|
|
109
|
+
|| ORCHESTRATOR_STATE_FILES.includes(filePath);
|
|
110
|
+
const continuityState = RUN_CONTINUITY_STATE_FILES.includes(filePath)
|
|
111
|
+
|| pathMatchesAnyRoot(filePath, RUN_CONTINUITY_DIRECTORY_ROOTS);
|
|
112
|
+
const baselineExempt = operational
|
|
113
|
+
|| continuityState
|
|
114
|
+
|| pathMatchesAnyPrefix(filePath, BASELINE_EXEMPT_PATH_PREFIXES);
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
operational,
|
|
118
|
+
baselineExempt,
|
|
119
|
+
continuityState,
|
|
120
|
+
projectOwned: !baselineExempt,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
87
124
|
/**
|
|
88
125
|
* Check whether a file path belongs to orchestrator-owned operational state.
|
|
89
126
|
* These paths are excluded from actor-attributed observation.
|
|
90
127
|
*/
|
|
91
128
|
export function isOperationalPath(filePath) {
|
|
92
|
-
return
|
|
93
|
-
|| ORCHESTRATOR_STATE_FILES.includes(filePath);
|
|
129
|
+
return classifyRepoPath(filePath).operational;
|
|
94
130
|
}
|
|
95
131
|
|
|
96
132
|
export function isBaselineExemptPath(filePath) {
|
|
97
|
-
return
|
|
98
|
-
|
|
133
|
+
return classifyRepoPath(filePath).baselineExempt;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function isRunContinuityPath(filePath) {
|
|
137
|
+
return classifyRepoPath(filePath).continuityState;
|
|
99
138
|
}
|
|
100
139
|
|
|
101
140
|
export function normalizeCheckpointableFiles(filesChanged) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { resolveAcceptedTurnHistoryReference } from './accepted-turn-history.js';
|
|
4
|
+
import { queryAcceptedTurnHistory, resolveAcceptedTurnHistoryReference } from './accepted-turn-history.js';
|
|
5
5
|
import { emitRunEvent } from './run-events.js';
|
|
6
6
|
import { safeWriteJson } from './safe-write.js';
|
|
7
|
-
import { normalizeCheckpointableFiles } from './repo-observer.js';
|
|
7
|
+
import { checkCleanBaseline, normalizeCheckpointableFiles } from './repo-observer.js';
|
|
8
8
|
|
|
9
9
|
const STATE_PATH = '.agentxchain/state.json';
|
|
10
10
|
const HISTORY_PATH = '.agentxchain/history.jsonl';
|
|
@@ -57,6 +57,68 @@ function normalizeFilesChanged(filesChanged) {
|
|
|
57
57
|
return normalizeCheckpointableFiles(filesChanged);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
function supportsLegacyFilesChangedRecovery(entry) {
|
|
61
|
+
const artifactType = entry?.artifact?.type;
|
|
62
|
+
return artifactType === 'workspace' || artifactType === 'patch';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getActorDirtyFiles(root, dirtyFiles = null) {
|
|
66
|
+
if (Array.isArray(dirtyFiles)) {
|
|
67
|
+
return normalizeFilesChanged(dirtyFiles);
|
|
68
|
+
}
|
|
69
|
+
const cleanCheck = checkCleanBaseline(root, 'authoritative');
|
|
70
|
+
return cleanCheck.clean ? [] : normalizeFilesChanged(cleanCheck.dirty_files);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function recoverLegacyCheckpointFiles(root, entry, opts = {}) {
|
|
74
|
+
if (!supportsLegacyFilesChangedRecovery(entry) || entry?.checkpoint_sha) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const state = readState(root);
|
|
79
|
+
if (state && Object.keys(state.active_turns || {}).length > 0) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const acceptedHistory = queryAcceptedTurnHistory(root);
|
|
84
|
+
if (acceptedHistory[0]?.turn_id !== entry?.turn_id) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return getActorDirtyFiles(root, opts.dirtyFiles);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function persistRecoveredFilesChanged(root, turnId, recoveredFiles) {
|
|
92
|
+
const normalizedRecoveredFiles = normalizeFilesChanged(recoveredFiles);
|
|
93
|
+
if (normalizedRecoveredFiles.length === 0) return;
|
|
94
|
+
|
|
95
|
+
const recoveredAt = new Date().toISOString();
|
|
96
|
+
const nextEntries = readHistoryEntries(root).map((historyEntry) => {
|
|
97
|
+
if (historyEntry.turn_id !== turnId) {
|
|
98
|
+
return historyEntry;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const observedArtifact = historyEntry?.observed_artifact && typeof historyEntry.observed_artifact === 'object'
|
|
102
|
+
? historyEntry.observed_artifact
|
|
103
|
+
: null;
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
...historyEntry,
|
|
107
|
+
files_changed: normalizedRecoveredFiles,
|
|
108
|
+
files_changed_recovered_at: recoveredAt,
|
|
109
|
+
files_changed_recovery_source: 'legacy_dirty_worktree',
|
|
110
|
+
observed_artifact: observedArtifact
|
|
111
|
+
? {
|
|
112
|
+
...observedArtifact,
|
|
113
|
+
files_changed: normalizedRecoveredFiles,
|
|
114
|
+
}
|
|
115
|
+
: observedArtifact,
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
writeHistoryEntries(root, nextEntries);
|
|
120
|
+
}
|
|
121
|
+
|
|
60
122
|
function extractGitError(err) {
|
|
61
123
|
const stderr = typeof err?.stderr === 'string' ? err.stderr.trim() : '';
|
|
62
124
|
const stdout = typeof err?.stdout === 'string' ? err.stdout.trim() : '';
|
|
@@ -88,15 +150,22 @@ export function detectPendingCheckpoint(root, dirtyFiles = []) {
|
|
|
88
150
|
if (entry.checkpoint_sha) return { required: false };
|
|
89
151
|
|
|
90
152
|
const turnFiles = normalizeFilesChanged(entry.files_changed);
|
|
91
|
-
|
|
153
|
+
const recoveredFiles = turnFiles.length === 0
|
|
154
|
+
? recoverLegacyCheckpointFiles(root, entry, { dirtyFiles: actorDirtyFiles })
|
|
155
|
+
: [];
|
|
156
|
+
const effectiveTurnFiles = turnFiles.length > 0 ? turnFiles : recoveredFiles;
|
|
157
|
+
if (effectiveTurnFiles.length === 0) return { required: false };
|
|
92
158
|
|
|
93
|
-
const dirtyOutsideTurn = actorDirtyFiles.filter((file) => !
|
|
159
|
+
const dirtyOutsideTurn = actorDirtyFiles.filter((file) => !effectiveTurnFiles.includes(file));
|
|
94
160
|
if (dirtyOutsideTurn.length > 0) return { required: false };
|
|
95
161
|
|
|
96
162
|
return {
|
|
97
163
|
required: true,
|
|
98
164
|
turn_id: entry.turn_id,
|
|
99
|
-
|
|
165
|
+
recovered_files_changed: recoveredFiles.length > 0 ? recoveredFiles : undefined,
|
|
166
|
+
message: recoveredFiles.length > 0
|
|
167
|
+
? `Accepted turn ${entry.turn_id} has legacy-empty files_changed history but still owns ${recoveredFiles.length} dirty actor file(s). Run agentxchain checkpoint-turn --turn ${entry.turn_id} to recover and checkpoint them.`
|
|
168
|
+
: `Accepted turn ${entry.turn_id} is not checkpointed yet. Run agentxchain checkpoint-turn --turn ${entry.turn_id} before assigning the next code-writing turn.`,
|
|
100
169
|
};
|
|
101
170
|
}
|
|
102
171
|
|
|
@@ -120,7 +189,17 @@ export function checkpointAcceptedTurn(root, opts = {}) {
|
|
|
120
189
|
};
|
|
121
190
|
}
|
|
122
191
|
|
|
123
|
-
const
|
|
192
|
+
const declaredFilesChanged = normalizeFilesChanged(entry.files_changed);
|
|
193
|
+
const recoveredFilesChanged = declaredFilesChanged.length === 0
|
|
194
|
+
? recoverLegacyCheckpointFiles(root, entry)
|
|
195
|
+
: [];
|
|
196
|
+
const filesChanged = declaredFilesChanged.length > 0
|
|
197
|
+
? declaredFilesChanged
|
|
198
|
+
: recoveredFilesChanged;
|
|
199
|
+
if (recoveredFilesChanged.length > 0) {
|
|
200
|
+
persistRecoveredFilesChanged(root, entry.turn_id, recoveredFilesChanged);
|
|
201
|
+
}
|
|
202
|
+
|
|
124
203
|
if (filesChanged.length === 0) {
|
|
125
204
|
return {
|
|
126
205
|
ok: true,
|