agentxchain 2.35.0 → 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 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
- export function render({ state, history, annotations, audit }) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "2.35.0",
3
+ "version": "2.36.0",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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() {
@@ -162,6 +164,25 @@ if ! grep -qE "^# Launch Evidence Report — AgentXchain v${ESCAPED_VERSION}" "$
162
164
  SURFACE_ERRORS+=("LAUNCH_EVIDENCE_REPORT.md title does not carry v${TARGET_VERSION}")
163
165
  fi
164
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
+
165
186
  if [[ "${#SURFACE_ERRORS[@]}" -gt 0 ]]; then
166
187
  echo "FAIL: ${#SURFACE_ERRORS[@]} version-surface(s) not aligned to ${TARGET_VERSION}:" >&2
167
188
  printf ' - %s\n' "${SURFACE_ERRORS[@]}" >&2
@@ -170,7 +191,7 @@ if [[ "${#SURFACE_ERRORS[@]}" -gt 0 ]]; then
170
191
  echo "create release identity when governed surfaces are stale." >&2
171
192
  exit 1
172
193
  fi
173
- echo " OK: all 7 governed version surfaces reference ${TARGET_VERSION}"
194
+ echo " OK: all 10 governed version surfaces reference ${TARGET_VERSION}"
174
195
 
175
196
  # 5. Update version files (no git operations)
176
197
  echo "[5/8] Updating version files..."
@@ -1,12 +1,8 @@
1
- import { existsSync } from 'fs';
2
- import { join } from 'path';
3
1
  import chalk from 'chalk';
4
2
  import { loadConfig, loadLock, loadProjectContext, loadProjectState, loadState } from '../lib/config.js';
5
3
  import { deriveRecoveryDescriptor } from '../lib/blocked-state.js';
6
4
  import { getActiveTurn, getActiveTurnCount, getActiveTurns } from '../lib/governed-state.js';
7
- import { readSessionCheckpoint } from '../lib/session-checkpoint.js';
8
-
9
- const SESSION_RECOVERY_PATH = '.agentxchain/SESSION_RECOVERY.md';
5
+ import { getContinuityStatus } from '../lib/continuity-status.js';
10
6
 
11
7
  export async function statusCommand(opts) {
12
8
  const context = loadProjectContext();
@@ -247,28 +243,6 @@ function renderGovernedStatus(context, opts) {
247
243
  console.log('');
248
244
  }
249
245
 
250
- function getContinuityStatus(root, state) {
251
- const checkpoint = readSessionCheckpoint(root);
252
- const recoveryReportPath = existsSync(join(root, SESSION_RECOVERY_PATH))
253
- ? SESSION_RECOVERY_PATH
254
- : null;
255
-
256
- if (!checkpoint && !recoveryReportPath) return null;
257
-
258
- const staleCheckpoint = !!(
259
- checkpoint?.run_id
260
- && state?.run_id
261
- && checkpoint.run_id !== state.run_id
262
- );
263
-
264
- return {
265
- checkpoint,
266
- stale_checkpoint: staleCheckpoint,
267
- recovery_report_path: recoveryReportPath,
268
- restart_recommended: !!state && !['blocked', 'completed', 'failed'].includes(state.status),
269
- };
270
- }
271
-
272
246
  function renderContinuityStatus(continuity, state) {
273
247
  if (!continuity) return;
274
248
 
@@ -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,10 +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';
13
14
  const SESSION_FILE = 'session.json';
15
+ const SESSION_RECOVERY_FILE = 'SESSION_RECOVERY.md';
14
16
  const HISTORY_FILE = 'history.jsonl';
15
17
  const LEDGER_FILE = 'decision-ledger.jsonl';
16
18
  const HOOK_AUDIT_FILE = 'hook-audit.jsonl';
@@ -44,6 +46,7 @@ export const RESOURCE_MAP = {
44
46
  export const FILE_TO_RESOURCE = Object.fromEntries(
45
47
  Object.entries(RESOURCE_MAP).map(([resource, file]) => [normalizeRelativePath(file), resource])
46
48
  );
49
+ FILE_TO_RESOURCE[normalizeRelativePath(SESSION_RECOVERY_FILE)] = '/api/continuity';
47
50
 
48
51
  export const WATCH_DIRECTORIES = [
49
52
  '',
@@ -86,6 +89,13 @@ export function readJsonlFile(agentxchainDir, filename) {
86
89
  * Read a resource by its API path. Returns { data, format } or null.
87
90
  */
88
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
+
89
99
  const filename = RESOURCE_MAP[resourcePath];
90
100
  if (!filename) return null;
91
101