agentxchain 2.34.2 → 2.36.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/dashboard/app.js +2 -1
- package/dashboard/components/timeline.js +37 -1
- package/package.json +1 -1
- package/scripts/release-bump.sh +89 -11
- package/src/commands/status.js +41 -0
- package/src/lib/continuity-status.js +27 -0
- package/src/lib/dashboard/state-reader.js +13 -1
- package/src/lib/export.js +1 -0
- package/src/lib/report.js +73 -0
package/dashboard/app.js
CHANGED
|
@@ -16,7 +16,7 @@ import { render as renderBlockers } from './components/blockers.js';
|
|
|
16
16
|
import { render as renderArtifacts } from './components/artifacts.js';
|
|
17
17
|
|
|
18
18
|
const VIEWS = {
|
|
19
|
-
timeline: { fetch: ['state', 'history', 'audit', 'annotations'], render: renderTimeline },
|
|
19
|
+
timeline: { fetch: ['state', 'continuity', 'history', 'audit', 'annotations'], render: renderTimeline },
|
|
20
20
|
ledger: { fetch: ['ledger'], render: renderLedger },
|
|
21
21
|
hooks: { fetch: ['audit', 'annotations'], render: renderHooks },
|
|
22
22
|
blocked: { fetch: ['state', 'audit', 'coordinatorState', 'coordinatorAudit'], render: renderBlocked },
|
|
@@ -29,6 +29,7 @@ const VIEWS = {
|
|
|
29
29
|
|
|
30
30
|
const API_MAP = {
|
|
31
31
|
state: '/api/state',
|
|
32
|
+
continuity: '/api/continuity',
|
|
32
33
|
history: '/api/history',
|
|
33
34
|
ledger: '/api/ledger',
|
|
34
35
|
audit: '/api/hooks/audit',
|
|
@@ -132,7 +132,41 @@ function renderTurnDetailPanel(turnId, annotations, audit) {
|
|
|
132
132
|
return html;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
function renderContinuityPanel(continuity) {
|
|
136
|
+
if (!continuity) return '';
|
|
137
|
+
|
|
138
|
+
const checkpoint = continuity.checkpoint;
|
|
139
|
+
const checkpointSummary = checkpoint?.last_checkpoint_at
|
|
140
|
+
? `${checkpoint.checkpoint_reason || 'unknown'} at ${checkpoint.last_checkpoint_at}`
|
|
141
|
+
: (checkpoint?.checkpoint_reason || 'No session checkpoint recorded');
|
|
142
|
+
|
|
143
|
+
let html = `<div class="section continuity-section"><h3>Continuity</h3><div class="turn-card">`;
|
|
144
|
+
|
|
145
|
+
if (checkpoint) {
|
|
146
|
+
html += `<div class="turn-detail"><span class="detail-label">Session:</span> <span class="mono">${esc(checkpoint.session_id || 'unknown')}</span></div>`;
|
|
147
|
+
html += `<div class="turn-detail"><span class="detail-label">Checkpoint:</span> ${esc(checkpointSummary)}</div>`;
|
|
148
|
+
html += `<div class="turn-detail"><span class="detail-label">Last turn:</span> <span class="mono">${esc(checkpoint.last_turn_id || 'none')}</span></div>`;
|
|
149
|
+
html += `<div class="turn-detail"><span class="detail-label">Last role:</span> ${esc(checkpoint.last_role || 'unknown')}</div>`;
|
|
150
|
+
if (continuity.stale_checkpoint) {
|
|
151
|
+
html += `<div class="turn-detail risks"><span class="detail-label">Warning:</span> checkpoint tracks <span class="mono">${esc(checkpoint.run_id || 'unknown')}</span>, but state.json remains source of truth.</div>`;
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
html += `<div class="turn-detail"><span class="detail-label">Checkpoint:</span> No session checkpoint recorded</div>`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (continuity.restart_recommended) {
|
|
158
|
+
html += `<div class="turn-detail"><span class="detail-label">Restart:</span> <span class="mono">agentxchain restart</span></div>`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (continuity.recovery_report_path) {
|
|
162
|
+
html += `<div class="turn-detail"><span class="detail-label">Report:</span> <span class="mono">${esc(continuity.recovery_report_path)}</span></div>`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
html += `</div></div>`;
|
|
166
|
+
return html;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function render({ state, continuity, history, annotations, audit }) {
|
|
136
170
|
if (!state) {
|
|
137
171
|
return `<div class="placeholder"><h2>No Run</h2><p>No governed run found. Start one with <code class="mono">agentxchain init --governed</code></p></div>`;
|
|
138
172
|
}
|
|
@@ -152,6 +186,8 @@ export function render({ state, history, annotations, audit }) {
|
|
|
152
186
|
</div>
|
|
153
187
|
</div>`;
|
|
154
188
|
|
|
189
|
+
html += renderContinuityPanel(continuity);
|
|
190
|
+
|
|
155
191
|
// Active turns
|
|
156
192
|
if (activeTurns.length > 0) {
|
|
157
193
|
html += `<div class="section"><h3>Active Turns</h3><div class="turn-list">`;
|
package/package.json
CHANGED
package/scripts/release-bump.sh
CHANGED
|
@@ -56,6 +56,8 @@ ALLOWED_RELEASE_PATHS=(
|
|
|
56
56
|
".agentxchain-conformance/capabilities.json"
|
|
57
57
|
"website-v2/docs/protocol-implementor-guide.mdx"
|
|
58
58
|
".planning/LAUNCH_EVIDENCE_REPORT.md"
|
|
59
|
+
"cli/homebrew/agentxchain.rb"
|
|
60
|
+
"cli/homebrew/README.md"
|
|
59
61
|
)
|
|
60
62
|
|
|
61
63
|
is_allowed_release_path() {
|
|
@@ -81,7 +83,7 @@ stage_if_present() {
|
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
# 1. Assert only allowed release-surface dirt is present
|
|
84
|
-
echo "[1/
|
|
86
|
+
echo "[1/8] Checking release-prep tree state..."
|
|
85
87
|
DISALLOWED_DIRTY=()
|
|
86
88
|
while IFS= read -r status_line; do
|
|
87
89
|
[[ -z "$status_line" ]] && continue
|
|
@@ -99,7 +101,7 @@ fi
|
|
|
99
101
|
echo " OK: tree contains only allowed release-prep changes"
|
|
100
102
|
|
|
101
103
|
# 2. Assert not already at target version
|
|
102
|
-
echo "[2/
|
|
104
|
+
echo "[2/8] Checking current version..."
|
|
103
105
|
CURRENT_VERSION=$(node -e "console.log(JSON.parse(require('fs').readFileSync('package.json','utf8')).version)")
|
|
104
106
|
if [[ "$CURRENT_VERSION" == "$TARGET_VERSION" ]]; then
|
|
105
107
|
echo "FAIL: package.json is already at ${TARGET_VERSION}. Cannot double-bump." >&2
|
|
@@ -108,20 +110,96 @@ fi
|
|
|
108
110
|
echo " OK: current version is ${CURRENT_VERSION}, bumping to ${TARGET_VERSION}"
|
|
109
111
|
|
|
110
112
|
# 3. Assert tag does not already exist
|
|
111
|
-
echo "[3/
|
|
113
|
+
echo "[3/8] Checking for existing tag..."
|
|
112
114
|
if git rev-parse "v${TARGET_VERSION}" >/dev/null 2>&1; then
|
|
113
115
|
echo "FAIL: tag v${TARGET_VERSION} already exists. Delete it first or choose a different version." >&2
|
|
114
116
|
exit 1
|
|
115
117
|
fi
|
|
116
118
|
echo " OK: tag v${TARGET_VERSION} does not exist"
|
|
117
119
|
|
|
118
|
-
# 4.
|
|
119
|
-
|
|
120
|
+
# 4. Pre-bump version-surface alignment guard
|
|
121
|
+
# Ensures all governed version surfaces already reference the target version
|
|
122
|
+
# BEFORE the bump commit is created. This catches stale drift that would
|
|
123
|
+
# otherwise only be discovered after minting local release identities.
|
|
124
|
+
echo "[4/8] Verifying version-surface alignment for ${TARGET_VERSION}..."
|
|
125
|
+
SURFACE_ERRORS=()
|
|
126
|
+
|
|
127
|
+
# 4a. CHANGELOG top heading
|
|
128
|
+
CHANGELOG_TOP=$(grep -m1 -E '^## [0-9]+\.[0-9]+\.[0-9]+$' "${REPO_ROOT}/cli/CHANGELOG.md" 2>/dev/null | sed 's/^## //' || true)
|
|
129
|
+
if [[ "$CHANGELOG_TOP" != "$TARGET_VERSION" ]]; then
|
|
130
|
+
SURFACE_ERRORS+=("CHANGELOG.md top heading is '${CHANGELOG_TOP:-missing}', expected '${TARGET_VERSION}'")
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# 4b. Release notes page exists
|
|
134
|
+
RELEASE_DOC_ID="v${TARGET_VERSION//./-}"
|
|
135
|
+
RELEASE_DOC_PATH="website-v2/docs/releases/${RELEASE_DOC_ID}.mdx"
|
|
136
|
+
if [[ ! -f "${REPO_ROOT}/${RELEASE_DOC_PATH}" ]]; then
|
|
137
|
+
SURFACE_ERRORS+=("release notes page missing: ${RELEASE_DOC_PATH}")
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
# 4c. Docs sidebar links the release page
|
|
141
|
+
if ! grep -q "'releases/${RELEASE_DOC_ID}'" "${REPO_ROOT}/website-v2/sidebars.ts" 2>/dev/null; then
|
|
142
|
+
SURFACE_ERRORS+=("sidebars.ts does not link 'releases/${RELEASE_DOC_ID}'")
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
# 4d. Homepage hero badge shows target version
|
|
146
|
+
if ! grep -q "v${TARGET_VERSION}" "${REPO_ROOT}/website-v2/src/pages/index.tsx" 2>/dev/null; then
|
|
147
|
+
SURFACE_ERRORS+=("homepage index.tsx does not contain 'v${TARGET_VERSION}'")
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
# 4e. Conformance capabilities version
|
|
151
|
+
CAPS_VERSION=$(node -e "try{console.log(JSON.parse(require('fs').readFileSync('${REPO_ROOT}/.agentxchain-conformance/capabilities.json','utf8')).version)}catch{console.log('missing')}" 2>/dev/null || echo "missing")
|
|
152
|
+
if [[ "$CAPS_VERSION" != "$TARGET_VERSION" ]]; then
|
|
153
|
+
SURFACE_ERRORS+=("capabilities.json version is '${CAPS_VERSION}', expected '${TARGET_VERSION}'")
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# 4f. Protocol implementor guide example
|
|
157
|
+
if ! grep -q "\"version\": \"${TARGET_VERSION}\"" "${REPO_ROOT}/website-v2/docs/protocol-implementor-guide.mdx" 2>/dev/null; then
|
|
158
|
+
SURFACE_ERRORS+=("protocol-implementor-guide.mdx does not contain '\"version\": \"${TARGET_VERSION}\"'")
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# 4g. Launch evidence report title
|
|
162
|
+
ESCAPED_VERSION="${TARGET_VERSION//./\\.}"
|
|
163
|
+
if ! grep -qE "^# Launch Evidence Report — AgentXchain v${ESCAPED_VERSION}" "${REPO_ROOT}/.planning/LAUNCH_EVIDENCE_REPORT.md" 2>/dev/null; then
|
|
164
|
+
SURFACE_ERRORS+=("LAUNCH_EVIDENCE_REPORT.md title does not carry v${TARGET_VERSION}")
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# 4h. Homebrew mirror formula version
|
|
168
|
+
HOMEBREW_MIRROR="${REPO_ROOT}/cli/homebrew/agentxchain.rb"
|
|
169
|
+
if [[ -f "$HOMEBREW_MIRROR" ]]; then
|
|
170
|
+
if ! grep -q "agentxchain-${TARGET_VERSION}\.tgz" "$HOMEBREW_MIRROR" 2>/dev/null; then
|
|
171
|
+
SURFACE_ERRORS+=("homebrew mirror formula does not reference agentxchain-${TARGET_VERSION}.tgz")
|
|
172
|
+
fi
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# 4i. Homebrew mirror maintainer README version
|
|
176
|
+
HOMEBREW_MIRROR_README="${REPO_ROOT}/cli/homebrew/README.md"
|
|
177
|
+
if [[ -f "$HOMEBREW_MIRROR_README" ]]; then
|
|
178
|
+
if ! grep -q -- "- version: \`${TARGET_VERSION}\`" "$HOMEBREW_MIRROR_README" 2>/dev/null; then
|
|
179
|
+
SURFACE_ERRORS+=("homebrew mirror README does not declare version ${TARGET_VERSION}")
|
|
180
|
+
fi
|
|
181
|
+
if ! grep -q "agentxchain-${TARGET_VERSION}\.tgz" "$HOMEBREW_MIRROR_README" 2>/dev/null; then
|
|
182
|
+
SURFACE_ERRORS+=("homebrew mirror README does not reference agentxchain-${TARGET_VERSION}.tgz")
|
|
183
|
+
fi
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
if [[ "${#SURFACE_ERRORS[@]}" -gt 0 ]]; then
|
|
187
|
+
echo "FAIL: ${#SURFACE_ERRORS[@]} version-surface(s) not aligned to ${TARGET_VERSION}:" >&2
|
|
188
|
+
printf ' - %s\n' "${SURFACE_ERRORS[@]}" >&2
|
|
189
|
+
echo "" >&2
|
|
190
|
+
echo "Fix these surfaces before running release-bump. The bump script refuses to" >&2
|
|
191
|
+
echo "create release identity when governed surfaces are stale." >&2
|
|
192
|
+
exit 1
|
|
193
|
+
fi
|
|
194
|
+
echo " OK: all 10 governed version surfaces reference ${TARGET_VERSION}"
|
|
195
|
+
|
|
196
|
+
# 5. Update version files (no git operations)
|
|
197
|
+
echo "[5/8] Updating version files..."
|
|
120
198
|
npm version "$TARGET_VERSION" --no-git-tag-version
|
|
121
199
|
echo " OK: package.json updated to ${TARGET_VERSION}"
|
|
122
200
|
|
|
123
|
-
#
|
|
124
|
-
echo "[
|
|
201
|
+
# 6. Stage version files
|
|
202
|
+
echo "[6/8] Staging version files..."
|
|
125
203
|
git add -- package.json
|
|
126
204
|
if [[ -f package-lock.json ]]; then
|
|
127
205
|
git add -- package-lock.json
|
|
@@ -131,8 +209,8 @@ for rel_path in "${ALLOWED_RELEASE_PATHS[@]}"; do
|
|
|
131
209
|
done
|
|
132
210
|
echo " OK: version files and allowed release surfaces staged"
|
|
133
211
|
|
|
134
|
-
#
|
|
135
|
-
echo "[
|
|
212
|
+
# 7. Create release commit
|
|
213
|
+
echo "[7/8] Creating release commit..."
|
|
136
214
|
git commit -m "${TARGET_VERSION}"
|
|
137
215
|
RELEASE_SHA=$(git rev-parse HEAD)
|
|
138
216
|
COMMIT_MSG=$(git log -1 --format=%s)
|
|
@@ -142,8 +220,8 @@ if [[ "$COMMIT_MSG" != "$TARGET_VERSION" ]]; then
|
|
|
142
220
|
fi
|
|
143
221
|
echo " OK: commit ${RELEASE_SHA:0:7} with message '${TARGET_VERSION}'"
|
|
144
222
|
|
|
145
|
-
#
|
|
146
|
-
echo "[
|
|
223
|
+
# 8. Create annotated tag
|
|
224
|
+
echo "[8/8] Creating annotated tag..."
|
|
147
225
|
git tag -a "v${TARGET_VERSION}" -m "v${TARGET_VERSION}"
|
|
148
226
|
TAG_SHA=$(git rev-parse "v${TARGET_VERSION}")
|
|
149
227
|
if [[ -z "$TAG_SHA" ]]; then
|
package/src/commands/status.js
CHANGED
|
@@ -2,6 +2,7 @@ import chalk from 'chalk';
|
|
|
2
2
|
import { loadConfig, loadLock, loadProjectContext, loadProjectState, loadState } from '../lib/config.js';
|
|
3
3
|
import { deriveRecoveryDescriptor } from '../lib/blocked-state.js';
|
|
4
4
|
import { getActiveTurn, getActiveTurnCount, getActiveTurns } from '../lib/governed-state.js';
|
|
5
|
+
import { getContinuityStatus } from '../lib/continuity-status.js';
|
|
5
6
|
|
|
6
7
|
export async function statusCommand(opts) {
|
|
7
8
|
const context = loadProjectContext();
|
|
@@ -74,6 +75,7 @@ export async function statusCommand(opts) {
|
|
|
74
75
|
function renderGovernedStatus(context, opts) {
|
|
75
76
|
const { root, config, version } = context;
|
|
76
77
|
const state = loadProjectState(root, config);
|
|
78
|
+
const continuity = getContinuityStatus(root, state);
|
|
77
79
|
|
|
78
80
|
if (opts.json) {
|
|
79
81
|
console.log(JSON.stringify({
|
|
@@ -82,6 +84,7 @@ function renderGovernedStatus(context, opts) {
|
|
|
82
84
|
template: config.template || 'generic',
|
|
83
85
|
config,
|
|
84
86
|
state,
|
|
87
|
+
continuity,
|
|
85
88
|
}, null, 2));
|
|
86
89
|
return;
|
|
87
90
|
}
|
|
@@ -101,6 +104,8 @@ function renderGovernedStatus(context, opts) {
|
|
|
101
104
|
}
|
|
102
105
|
console.log('');
|
|
103
106
|
|
|
107
|
+
renderContinuityStatus(continuity, state);
|
|
108
|
+
|
|
104
109
|
const activeTurnCount = getActiveTurnCount(state);
|
|
105
110
|
const activeTurns = getActiveTurns(state);
|
|
106
111
|
const singleActiveTurn = getActiveTurn(state);
|
|
@@ -238,6 +243,42 @@ function renderGovernedStatus(context, opts) {
|
|
|
238
243
|
console.log('');
|
|
239
244
|
}
|
|
240
245
|
|
|
246
|
+
function renderContinuityStatus(continuity, state) {
|
|
247
|
+
if (!continuity) return;
|
|
248
|
+
|
|
249
|
+
console.log(` ${chalk.dim('Continuity:')}`);
|
|
250
|
+
|
|
251
|
+
if (continuity.checkpoint) {
|
|
252
|
+
const checkpoint = continuity.checkpoint;
|
|
253
|
+
const checkpointSummary = checkpoint.last_checkpoint_at
|
|
254
|
+
? `${checkpoint.checkpoint_reason || 'unknown'} at ${checkpoint.last_checkpoint_at}`
|
|
255
|
+
: (checkpoint.checkpoint_reason || 'unknown');
|
|
256
|
+
console.log(` ${chalk.dim('Session:')} ${checkpoint.session_id || chalk.dim('unknown')}`);
|
|
257
|
+
console.log(` ${chalk.dim('Checkpoint:')} ${checkpointSummary}`);
|
|
258
|
+
console.log(` ${chalk.dim('Last turn:')} ${checkpoint.last_turn_id || chalk.dim('none')}`);
|
|
259
|
+
console.log(` ${chalk.dim('Last role:')} ${checkpoint.last_role || chalk.dim('unknown')}`);
|
|
260
|
+
if (continuity.stale_checkpoint) {
|
|
261
|
+
console.log(
|
|
262
|
+
` ${chalk.dim('Warning:')} ${chalk.yellow(
|
|
263
|
+
`session checkpoint tracks ${checkpoint.run_id}, but state.json tracks ${state?.run_id || 'unknown'}; state.json remains source of truth`
|
|
264
|
+
)}`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
console.log(` ${chalk.dim('Checkpoint:')} ${chalk.yellow('No session checkpoint recorded')}`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (continuity.restart_recommended) {
|
|
272
|
+
console.log(` ${chalk.dim('Restart:')} ${chalk.cyan('agentxchain restart')} (rebuild session context from disk)`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (continuity.recovery_report_path) {
|
|
276
|
+
console.log(` ${chalk.dim('Report:')} ${continuity.recovery_report_path}`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
console.log('');
|
|
280
|
+
}
|
|
281
|
+
|
|
241
282
|
function formatPhase(phase) {
|
|
242
283
|
const colors = { discovery: chalk.blue, build: chalk.green, qa: chalk.yellow, deploy: chalk.magenta, blocked: chalk.red };
|
|
243
284
|
return (colors[phase] || chalk.white)(phase);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { readSessionCheckpoint } from './session-checkpoint.js';
|
|
4
|
+
|
|
5
|
+
export const SESSION_RECOVERY_PATH = '.agentxchain/SESSION_RECOVERY.md';
|
|
6
|
+
|
|
7
|
+
export function getContinuityStatus(root, state) {
|
|
8
|
+
const checkpoint = readSessionCheckpoint(root);
|
|
9
|
+
const recoveryReportPath = existsSync(join(root, SESSION_RECOVERY_PATH))
|
|
10
|
+
? SESSION_RECOVERY_PATH
|
|
11
|
+
: null;
|
|
12
|
+
|
|
13
|
+
if (!checkpoint && !recoveryReportPath) return null;
|
|
14
|
+
|
|
15
|
+
const staleCheckpoint = !!(
|
|
16
|
+
checkpoint?.run_id
|
|
17
|
+
&& state?.run_id
|
|
18
|
+
&& checkpoint.run_id !== state.run_id
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
checkpoint,
|
|
23
|
+
stale_checkpoint: staleCheckpoint,
|
|
24
|
+
recovery_report_path: recoveryReportPath,
|
|
25
|
+
restart_recommended: !!state && !['blocked', 'completed', 'failed'].includes(state.status),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -7,9 +7,12 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { readFileSync, existsSync } from 'fs';
|
|
10
|
-
import { join, normalize } from 'path';
|
|
10
|
+
import { join, normalize, resolve } from 'path';
|
|
11
|
+
import { getContinuityStatus } from '../continuity-status.js';
|
|
11
12
|
|
|
12
13
|
const STATE_FILE = 'state.json';
|
|
14
|
+
const SESSION_FILE = 'session.json';
|
|
15
|
+
const SESSION_RECOVERY_FILE = 'SESSION_RECOVERY.md';
|
|
13
16
|
const HISTORY_FILE = 'history.jsonl';
|
|
14
17
|
const LEDGER_FILE = 'decision-ledger.jsonl';
|
|
15
18
|
const HOOK_AUDIT_FILE = 'hook-audit.jsonl';
|
|
@@ -23,6 +26,7 @@ const BARRIER_LEDGER_FILE = 'barrier-ledger.jsonl';
|
|
|
23
26
|
*/
|
|
24
27
|
export const RESOURCE_MAP = {
|
|
25
28
|
'/api/state': STATE_FILE,
|
|
29
|
+
'/api/continuity': SESSION_FILE,
|
|
26
30
|
'/api/history': HISTORY_FILE,
|
|
27
31
|
'/api/ledger': LEDGER_FILE,
|
|
28
32
|
'/api/hooks/audit': HOOK_AUDIT_FILE,
|
|
@@ -42,6 +46,7 @@ export const RESOURCE_MAP = {
|
|
|
42
46
|
export const FILE_TO_RESOURCE = Object.fromEntries(
|
|
43
47
|
Object.entries(RESOURCE_MAP).map(([resource, file]) => [normalizeRelativePath(file), resource])
|
|
44
48
|
);
|
|
49
|
+
FILE_TO_RESOURCE[normalizeRelativePath(SESSION_RECOVERY_FILE)] = '/api/continuity';
|
|
45
50
|
|
|
46
51
|
export const WATCH_DIRECTORIES = [
|
|
47
52
|
'',
|
|
@@ -84,6 +89,13 @@ export function readJsonlFile(agentxchainDir, filename) {
|
|
|
84
89
|
* Read a resource by its API path. Returns { data, format } or null.
|
|
85
90
|
*/
|
|
86
91
|
export function readResource(agentxchainDir, resourcePath) {
|
|
92
|
+
if (resourcePath === '/api/continuity') {
|
|
93
|
+
const root = resolve(agentxchainDir, '..');
|
|
94
|
+
const state = readJsonFile(agentxchainDir, STATE_FILE);
|
|
95
|
+
const data = getContinuityStatus(root, state);
|
|
96
|
+
return { data, format: 'json' };
|
|
97
|
+
}
|
|
98
|
+
|
|
87
99
|
const filename = RESOURCE_MAP[resourcePath];
|
|
88
100
|
if (!filename) return null;
|
|
89
101
|
|
package/src/lib/export.js
CHANGED
package/src/lib/report.js
CHANGED
|
@@ -503,6 +503,30 @@ export function extractWorkflowKitArtifacts(artifact) {
|
|
|
503
503
|
.sort((a, b) => a.path.localeCompare(b.path, 'en'));
|
|
504
504
|
}
|
|
505
505
|
|
|
506
|
+
function extractContinuityMetadata(artifact) {
|
|
507
|
+
const checkpoint = extractFileData(artifact, '.agentxchain/session.json');
|
|
508
|
+
if (!checkpoint || typeof checkpoint !== 'object') return null;
|
|
509
|
+
|
|
510
|
+
const runId = artifact.summary?.run_id || artifact.state?.run_id || null;
|
|
511
|
+
const staleCheckpoint = !!(
|
|
512
|
+
checkpoint.run_id
|
|
513
|
+
&& runId
|
|
514
|
+
&& checkpoint.run_id !== runId
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
return {
|
|
518
|
+
session_id: checkpoint.session_id || null,
|
|
519
|
+
run_id: checkpoint.run_id || null,
|
|
520
|
+
started_at: checkpoint.started_at || null,
|
|
521
|
+
last_checkpoint_at: checkpoint.last_checkpoint_at || null,
|
|
522
|
+
last_turn_id: checkpoint.last_turn_id || null,
|
|
523
|
+
last_phase: checkpoint.last_phase || null,
|
|
524
|
+
last_role: checkpoint.last_role || null,
|
|
525
|
+
checkpoint_reason: checkpoint.checkpoint_reason || null,
|
|
526
|
+
stale_checkpoint: staleCheckpoint,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
506
530
|
function buildRunSubject(artifact) {
|
|
507
531
|
const activeTurns = artifact.summary?.active_turn_ids || [];
|
|
508
532
|
const retainedTurns = artifact.summary?.retained_turn_ids || [];
|
|
@@ -519,6 +543,7 @@ function buildRunSubject(artifact) {
|
|
|
519
543
|
const gateSummary = extractGateSummary(artifact);
|
|
520
544
|
const intakeLinks = extractIntakeLinks(artifact);
|
|
521
545
|
const recoverySummary = extractRecoverySummary(artifact);
|
|
546
|
+
const continuity = extractContinuityMetadata(artifact);
|
|
522
547
|
|
|
523
548
|
return {
|
|
524
549
|
kind: 'governed_run',
|
|
@@ -550,6 +575,7 @@ function buildRunSubject(artifact) {
|
|
|
550
575
|
gate_summary: gateSummary,
|
|
551
576
|
intake_links: intakeLinks,
|
|
552
577
|
recovery_summary: recoverySummary,
|
|
578
|
+
continuity,
|
|
553
579
|
workflow_kit_artifacts: extractWorkflowKitArtifacts(artifact),
|
|
554
580
|
},
|
|
555
581
|
artifacts: {
|
|
@@ -620,6 +646,7 @@ function buildCoordinatorSubject(artifact) {
|
|
|
620
646
|
base.hook_summary = extractHookSummary(childExport);
|
|
621
647
|
base.gate_summary = extractGateSummary(childExport);
|
|
622
648
|
base.recovery_summary = extractRecoverySummary(childExport);
|
|
649
|
+
base.continuity = extractContinuityMetadata(childExport);
|
|
623
650
|
base.blocked_on = childExport.state?.blocked_on || null;
|
|
624
651
|
return base;
|
|
625
652
|
});
|
|
@@ -843,6 +870,18 @@ export function formatGovernanceReportText(report) {
|
|
|
843
870
|
lines.push(` Turn retained: ${run.recovery_summary.turn_retained == null ? 'n/a' : yesNo(run.recovery_summary.turn_retained)}`);
|
|
844
871
|
}
|
|
845
872
|
|
|
873
|
+
if (run.continuity) {
|
|
874
|
+
lines.push('', 'Continuity:');
|
|
875
|
+
lines.push(` Session: ${run.continuity.session_id || 'unknown'}`);
|
|
876
|
+
lines.push(` Checkpoint: ${run.continuity.checkpoint_reason || 'unknown'} at ${run.continuity.last_checkpoint_at || 'n/a'}`);
|
|
877
|
+
lines.push(` Last turn: ${run.continuity.last_turn_id || 'none'}`);
|
|
878
|
+
lines.push(` Last role: ${run.continuity.last_role || 'unknown'}`);
|
|
879
|
+
lines.push(` Last phase: ${run.continuity.last_phase || 'unknown'}`);
|
|
880
|
+
if (run.continuity.stale_checkpoint) {
|
|
881
|
+
lines.push(` WARNING: checkpoint tracks run ${run.continuity.run_id}, but export tracks ${run.run_id}`);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
846
885
|
if (Array.isArray(run.workflow_kit_artifacts) && run.workflow_kit_artifacts.length > 0) {
|
|
847
886
|
lines.push('', `Workflow Artifacts (${run.phase || 'unknown'} phase):`);
|
|
848
887
|
for (const art of run.workflow_kit_artifacts) {
|
|
@@ -985,6 +1024,17 @@ export function formatGovernanceReportText(report) {
|
|
|
985
1024
|
if (repo.recovery_summary) {
|
|
986
1025
|
repoLines.push(` Recovery: ${repo.recovery_summary.category || 'unknown'} — ${repo.recovery_summary.typed_reason || 'unknown'} (owner: ${repo.recovery_summary.owner || 'unknown'})`);
|
|
987
1026
|
}
|
|
1027
|
+
if (repo.continuity) {
|
|
1028
|
+
repoLines.push(' Continuity:');
|
|
1029
|
+
repoLines.push(` Session: ${repo.continuity.session_id || 'unknown'}`);
|
|
1030
|
+
repoLines.push(` Checkpoint: ${repo.continuity.checkpoint_reason || 'unknown'} at ${repo.continuity.last_checkpoint_at || 'n/a'}`);
|
|
1031
|
+
repoLines.push(` Last turn: ${repo.continuity.last_turn_id || 'none'}`);
|
|
1032
|
+
repoLines.push(` Last role: ${repo.continuity.last_role || 'unknown'}`);
|
|
1033
|
+
repoLines.push(` Last phase: ${repo.continuity.last_phase || 'unknown'}`);
|
|
1034
|
+
if (repo.continuity.stale_checkpoint) {
|
|
1035
|
+
repoLines.push(` WARNING: checkpoint tracks run ${repo.continuity.run_id}, but repo export tracks ${repo.run_id}`);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
988
1038
|
return repoLines;
|
|
989
1039
|
}));
|
|
990
1040
|
return lines.join('\n');
|
|
@@ -1110,6 +1160,18 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1110
1160
|
lines.push(`- Turn retained: \`${run.recovery_summary.turn_retained == null ? 'n/a' : yesNo(run.recovery_summary.turn_retained)}\``);
|
|
1111
1161
|
}
|
|
1112
1162
|
|
|
1163
|
+
if (run.continuity) {
|
|
1164
|
+
lines.push('', '## Continuity', '');
|
|
1165
|
+
lines.push(`- Session: \`${run.continuity.session_id || 'unknown'}\``);
|
|
1166
|
+
lines.push(`- Checkpoint: \`${run.continuity.checkpoint_reason || 'unknown'}\` at \`${run.continuity.last_checkpoint_at || 'n/a'}\``);
|
|
1167
|
+
lines.push(`- Last turn: \`${run.continuity.last_turn_id || 'none'}\``);
|
|
1168
|
+
lines.push(`- Last role: \`${run.continuity.last_role || 'unknown'}\``);
|
|
1169
|
+
lines.push(`- Last phase: \`${run.continuity.last_phase || 'unknown'}\``);
|
|
1170
|
+
if (run.continuity.stale_checkpoint) {
|
|
1171
|
+
lines.push(`- **Warning:** checkpoint tracks run \`${run.continuity.run_id}\`, but export tracks \`${run.run_id}\``);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1113
1175
|
if (Array.isArray(run.workflow_kit_artifacts) && run.workflow_kit_artifacts.length > 0) {
|
|
1114
1176
|
lines.push('', '## Workflow Artifacts', '');
|
|
1115
1177
|
lines.push(`Phase: \`${run.phase || 'unknown'}\``, '');
|
|
@@ -1258,6 +1320,17 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1258
1320
|
if (repo.recovery_summary) {
|
|
1259
1321
|
repoLines.push('', '#### Recovery', '', `- Category: \`${repo.recovery_summary.category || 'unknown'}\``, `- Typed reason: \`${repo.recovery_summary.typed_reason || 'unknown'}\``, `- Owner: \`${repo.recovery_summary.owner || 'unknown'}\``, `- Action: \`${repo.recovery_summary.recovery_action || 'n/a'}\``);
|
|
1260
1322
|
}
|
|
1323
|
+
if (repo.continuity) {
|
|
1324
|
+
repoLines.push('', '#### Continuity', '');
|
|
1325
|
+
repoLines.push(`- Session: \`${repo.continuity.session_id || 'unknown'}\``);
|
|
1326
|
+
repoLines.push(`- Checkpoint: \`${repo.continuity.checkpoint_reason || 'unknown'}\` at \`${repo.continuity.last_checkpoint_at || 'n/a'}\``);
|
|
1327
|
+
repoLines.push(`- Last turn: \`${repo.continuity.last_turn_id || 'none'}\``);
|
|
1328
|
+
repoLines.push(`- Last role: \`${repo.continuity.last_role || 'unknown'}\``);
|
|
1329
|
+
repoLines.push(`- Last phase: \`${repo.continuity.last_phase || 'unknown'}\``);
|
|
1330
|
+
if (repo.continuity.stale_checkpoint) {
|
|
1331
|
+
repoLines.push(`- **Warning:** checkpoint tracks run \`${repo.continuity.run_id}\`, but repo export tracks \`${repo.run_id}\``);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1261
1334
|
repoLines.push('');
|
|
1262
1335
|
return repoLines;
|
|
1263
1336
|
}));
|