agentxchain 2.56.0 → 2.57.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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "2.56.0",
3
+ "version": "2.57.0",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
@@ -976,6 +976,11 @@ async function initGoverned(opts) {
976
976
  console.log(` ${chalk.bold('agentxchain step')} ${chalk.dim('# run the first governed turn')}`);
977
977
  console.log(` ${chalk.bold('agentxchain status')} ${chalk.dim('# inspect phase, gate, and turn state')}`);
978
978
  console.log('');
979
+ if (!config?.project?.goal) {
980
+ console.log(` ${chalk.dim('Tip:')} Add a project goal to guide agent context:`);
981
+ console.log(` ${chalk.bold('agentxchain init --governed --goal "Build a ..."')} ${chalk.dim('# or set project.goal in agentxchain.json')}`);
982
+ console.log('');
983
+ }
979
984
  console.log(` ${chalk.dim('Guide:')} https://agentxchain.dev/docs/getting-started`);
980
985
  console.log('');
981
986
  }
@@ -57,6 +57,7 @@ export function buildInheritedContext(root, parentRunId) {
57
57
  parent_phases_completed: parentEntry.phases_completed || [],
58
58
  parent_roles_used: parentEntry.roles_used || [],
59
59
  parent_blocked_reason: parentEntry.blocked_reason || null,
60
+ parent_retrospective: parentEntry.retrospective || null,
60
61
  recent_decisions: recentDecisions,
61
62
  recent_accepted_turns: acceptedTurns,
62
63
  inherited_at: new Date().toISOString(),
@@ -93,6 +94,20 @@ export function renderInheritedContextMarkdown(inheritedContext, compact = false
93
94
  }
94
95
  lines.push('');
95
96
 
97
+ if (!compact && inheritedContext.parent_retrospective) {
98
+ lines.push('### Parent Retrospective');
99
+ lines.push('');
100
+ lines.push(`- **Headline:** ${inheritedContext.parent_retrospective.headline || 'n/a'}`);
101
+ lines.push(`- **Terminal reason:** ${inheritedContext.parent_retrospective.terminal_reason || 'unknown'}`);
102
+ if (inheritedContext.parent_retrospective.next_operator_action) {
103
+ lines.push(`- **Next operator action:** ${inheritedContext.parent_retrospective.next_operator_action}`);
104
+ }
105
+ if (inheritedContext.parent_retrospective.follow_on_hint) {
106
+ lines.push(`- **Follow-on hint:** ${inheritedContext.parent_retrospective.follow_on_hint}`);
107
+ }
108
+ lines.push('');
109
+ }
110
+
96
111
  if (!compact && inheritedContext.recent_decisions?.length) {
97
112
  lines.push('### Recent Decisions');
98
113
  lines.push('');
@@ -153,6 +168,7 @@ function buildPartialContext(parentRunId, parentEntry, decisions, turns, warning
153
168
  parent_phases_completed: parentEntry?.phases_completed || [],
154
169
  parent_roles_used: parentEntry?.roles_used || [],
155
170
  parent_blocked_reason: parentEntry?.blocked_reason || null,
171
+ parent_retrospective: parentEntry?.retrospective || null,
156
172
  recent_decisions: decisions,
157
173
  recent_accepted_turns: turns,
158
174
  inherited_at: new Date().toISOString(),
@@ -10,6 +10,7 @@
10
10
  import { readFileSync, appendFileSync, existsSync, mkdirSync } from 'fs';
11
11
  import { join, dirname } from 'path';
12
12
  import { normalizeRunProvenance } from './run-provenance.js';
13
+ import { deriveRecoveryDescriptor } from './blocked-state.js';
13
14
 
14
15
  const RUN_HISTORY_PATH = '.agentxchain/run-history.jsonl';
15
16
  const HISTORY_PATH = '.agentxchain/history.jsonl';
@@ -83,6 +84,12 @@ export function recordRunHistory(root, state, config, status) {
83
84
  connector_used: connectorUsed,
84
85
  model_used: modelUsed,
85
86
  provenance: normalizeRunProvenance(state?.provenance),
87
+ retrospective: buildRunRetrospective({
88
+ state,
89
+ config,
90
+ status,
91
+ historyEntries,
92
+ }),
86
93
  inheritance_snapshot: {
87
94
  recent_decisions: buildRecentDecisionSnapshot(ledgerEntries),
88
95
  recent_accepted_turns: buildRecentAcceptedTurnSnapshot(historyEntries),
@@ -300,7 +307,7 @@ function buildRecentDecisionSnapshot(entries) {
300
307
 
301
308
  function buildRecentAcceptedTurnSnapshot(entries) {
302
309
  return entries
303
- .filter((entry) => entry.status === 'accepted')
310
+ .filter((entry) => entry?.status !== 'rejected' && typeof entry?.turn_id === 'string' && typeof entry?.role === 'string')
304
311
  .slice(-MAX_INHERITANCE_TURNS)
305
312
  .map((entry) => ({
306
313
  turn_id: entry.turn_id || null,
@@ -309,3 +316,57 @@ function buildRecentAcceptedTurnSnapshot(entries) {
309
316
  phase: entry.phase || null,
310
317
  }));
311
318
  }
319
+
320
+ function buildRunRetrospective({ state, config, status, historyEntries }) {
321
+ const acceptedTurns = historyEntries.filter((entry) => entry && typeof entry === 'object');
322
+ const lastAcceptedTurn = acceptedTurns[acceptedTurns.length - 1] || null;
323
+ const recovery = status === 'blocked'
324
+ ? deriveRecoveryDescriptor(state, config)
325
+ : null;
326
+
327
+ return {
328
+ headline: buildRetrospectiveHeadline({ status, lastAcceptedTurn, recovery, acceptedTurns }),
329
+ terminal_reason: status === 'completed'
330
+ ? 'completed'
331
+ : recovery?.typed_reason || 'blocked',
332
+ next_operator_action: status === 'blocked'
333
+ ? recovery?.recovery_action || null
334
+ : null,
335
+ follow_on_hint: status === 'completed'
336
+ ? buildFollowOnHint(state, lastAcceptedTurn)
337
+ : null,
338
+ };
339
+ }
340
+
341
+ function buildRetrospectiveHeadline({ status, lastAcceptedTurn, recovery, acceptedTurns }) {
342
+ if (status === 'blocked') {
343
+ if (typeof recovery?.detail === 'string' && recovery.detail.trim()) {
344
+ return recovery.detail.trim();
345
+ }
346
+ if (typeof lastAcceptedTurn?.summary === 'string' && lastAcceptedTurn.summary.trim()) {
347
+ return `Run blocked after: ${lastAcceptedTurn.summary.trim()}`;
348
+ }
349
+ return 'Run blocked.';
350
+ }
351
+
352
+ if (typeof lastAcceptedTurn?.summary === 'string' && lastAcceptedTurn.summary.trim()) {
353
+ return lastAcceptedTurn.summary.trim();
354
+ }
355
+
356
+ return `Run completed after ${acceptedTurns.length} accepted turn(s).`;
357
+ }
358
+
359
+ function buildFollowOnHint(state, lastAcceptedTurn) {
360
+ const runId = state?.run_id;
361
+ if (typeof runId !== 'string' || !runId.trim()) {
362
+ return null;
363
+ }
364
+
365
+ const base = `If more scope remains, start a child run with \`agentxchain run --continue-from ${runId} --inherit-context\`.`;
366
+ const suggestedRole = lastAcceptedTurn?.proposed_next_role;
367
+ if (typeof suggestedRole === 'string' && suggestedRole.trim() && suggestedRole !== 'human') {
368
+ return `${base} The last accepted turn suggested \`${suggestedRole}\` next.`;
369
+ }
370
+
371
+ return base;
372
+ }