agentxchain 2.41.0 → 2.43.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.41.0",
3
+ "version": "2.43.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
+ "website-v2/static/llms.txt"
60
+ "website-v2/static/sitemap.xml"
59
61
  "cli/homebrew/agentxchain.rb"
60
62
  "cli/homebrew/README.md"
61
63
  )
@@ -168,6 +170,17 @@ if ! grep -qE "^# Launch Evidence Report — AgentXchain v${ESCAPED_VERSION}" "$
168
170
  SURFACE_ERRORS+=("LAUNCH_EVIDENCE_REPORT.md title does not carry v${TARGET_VERSION}")
169
171
  fi
170
172
 
173
+ # 4h. llms.txt must list the current release notes route
174
+ CURRENT_RELEASE_ROUTE="/docs/releases/${RELEASE_DOC_ID}"
175
+ if ! grep -q "${CURRENT_RELEASE_ROUTE}" "${REPO_ROOT}/website-v2/static/llms.txt" 2>/dev/null; then
176
+ SURFACE_ERRORS+=("website-v2/static/llms.txt does not list '${CURRENT_RELEASE_ROUTE}'")
177
+ fi
178
+
179
+ # 4i. sitemap.xml must list the current release notes route
180
+ if ! grep -q "${CURRENT_RELEASE_ROUTE}" "${REPO_ROOT}/website-v2/static/sitemap.xml" 2>/dev/null; then
181
+ SURFACE_ERRORS+=("website-v2/static/sitemap.xml does not list '${CURRENT_RELEASE_ROUTE}'")
182
+ fi
183
+
171
184
  if [[ "${#SURFACE_ERRORS[@]}" -gt 0 ]]; then
172
185
  echo "FAIL: ${#SURFACE_ERRORS[@]} version-surface(s) not aligned to ${TARGET_VERSION}:" >&2
173
186
  printf ' - %s\n' "${SURFACE_ERRORS[@]}" >&2
@@ -176,7 +189,7 @@ if [[ "${#SURFACE_ERRORS[@]}" -gt 0 ]]; then
176
189
  echo "create release identity when governed surfaces are stale." >&2
177
190
  exit 1
178
191
  fi
179
- echo " OK: all 7 governed version surfaces reference ${TARGET_VERSION}"
192
+ echo " OK: all 9 governed version surfaces reference ${TARGET_VERSION}"
180
193
 
181
194
  # 5. Auto-align Homebrew mirror to target version
182
195
  # The formula URL and README version/tarball are updated automatically.
@@ -79,6 +79,7 @@ export function validateStagedTurnResult(root, state, config, opts = {}) {
79
79
  const activeTurn = getActiveTurn(state) || state.current_turn;
80
80
  if (activeTurn) {
81
81
  const roleKey = activeTurn.assigned_role || activeTurn.role;
82
+ normContext.assignedRole = roleKey;
82
83
  const roleConfig = config?.roles?.[roleKey];
83
84
  if (roleConfig) {
84
85
  normContext.writeAuthority = roleConfig.write_authority;
@@ -562,6 +563,23 @@ export function normalizeTurnResult(tr, config, context = {}) {
562
563
  }
563
564
 
564
565
  const normalized = { ...tr };
566
+ const routing = config?.routing;
567
+ const phaseNames = routing ? Object.keys(routing) : [];
568
+ const currentPhase = context.phase;
569
+ const currentPhaseIndex = currentPhase ? phaseNames.indexOf(currentPhase) : -1;
570
+ const isKnownPhase = currentPhaseIndex >= 0;
571
+ const isTerminalPhase = isKnownPhase && currentPhaseIndex === phaseNames.length - 1;
572
+ const nextPhase = isKnownPhase && currentPhaseIndex + 1 < phaseNames.length
573
+ ? phaseNames[currentPhaseIndex + 1]
574
+ : null;
575
+ const allowedNextRoles = isKnownPhase ? (routing?.[currentPhase]?.allowed_next_roles || []) : [];
576
+ const assignedRole = context.assignedRole || normalized.role || null;
577
+ const isReviewOnly = context.writeAuthority === 'review_only';
578
+
579
+ const pickAllowedRoleFallback = () => {
580
+ if (allowedNextRoles.length === 0) return null;
581
+ return allowedNextRoles.find((role) => role !== assignedRole) || allowedNextRoles[0] || null;
582
+ };
565
583
 
566
584
  // ── Rule 0: infer missing status only when intent is unambiguous ──────
567
585
  if (!('status' in normalized)) {
@@ -604,7 +622,6 @@ export function normalizeTurnResult(tr, config, context = {}) {
604
622
  }
605
623
 
606
624
  // ── Rule 2: exit-gate-as-phase auto-correction ────────────────────────
607
- const routing = config?.routing;
608
625
  const gates = config?.gates;
609
626
  if (
610
627
  typeof normalized.phase_transition_request === 'string' &&
@@ -643,33 +660,117 @@ export function normalizeTurnResult(tr, config, context = {}) {
643
660
  }
644
661
  }
645
662
 
646
- // ── Rule 3: review_only terminal needs_human → run_completion_request ──
663
+ // ── Rule 3: review_only needs_human → lifecycle correction ──────────
664
+ // review_only roles cannot perform work that genuinely requires human
665
+ // intervention. If the model says "needs_human" with an affirmative,
666
+ // non-blocker reason, correct to the appropriate lifecycle signal:
667
+ // terminal phase → run_completion_request, non-terminal → phase_transition.
647
668
  if (
648
669
  context.writeAuthority === 'review_only' &&
649
670
  context.phase &&
650
671
  routing &&
651
672
  normalized.status === 'needs_human' &&
652
- normalized.run_completion_request !== false
673
+ normalized.run_completion_request !== false &&
674
+ typeof normalized.needs_human_reason === 'string'
653
675
  ) {
654
- const phaseNames = Object.keys(routing);
655
- const isTerminal = phaseNames.indexOf(context.phase) === phaseNames.length - 1;
656
- if (isTerminal && typeof normalized.needs_human_reason === 'string') {
657
- const reason = normalized.needs_human_reason.toLowerCase();
658
- const affirmativeSignals = /\b(approv|ship|release|sign.?off|no.?block|ready|pass|good|accept|green.?light)\b/i;
659
- const blockerSignals = /\b(critical|security|fail|block|cannot|must.?fix|regression|vulnerab|reject|unsafe|broken)\b/i;
660
- const isAffirmative = affirmativeSignals.test(reason);
661
- const isBlocker = blockerSignals.test(reason);
662
- if (isAffirmative && !isBlocker) {
676
+ const reason = normalized.needs_human_reason.toLowerCase();
677
+ const affirmativeSignals = /\b(approv|ship|release|sign.?off|no.?block|ready|pass|good|accept|green.?light|proceed|move.?forward|complet|done|lgtm|satisf|recommend)\b/i;
678
+ const blockerSignals = /\b(critical|security|fail|block|cannot|must.?fix|regression|vulnerab|reject|unsafe|broken)\b/i;
679
+ const isAffirmative = affirmativeSignals.test(reason);
680
+ const isBlocker = blockerSignals.test(reason);
681
+ if (isAffirmative && !isBlocker) {
682
+ if (isTerminalPhase) {
663
683
  corrections.push(
664
684
  `status: corrected review_only terminal "needs_human" to run_completion_request — reason indicated ship readiness ("${normalized.needs_human_reason.slice(0, 80)}"), not a genuine blocker`
665
685
  );
666
686
  normalized.status = 'completed';
667
687
  normalized.run_completion_request = true;
668
688
  delete normalized.needs_human_reason;
689
+ } else if (nextPhase) {
690
+ corrections.push(
691
+ `status: corrected review_only "needs_human" to phase_transition_request "${nextPhase}" — reason indicated forward progress ("${normalized.needs_human_reason.slice(0, 80)}"), not a genuine blocker`
692
+ );
693
+ normalized.status = 'completed';
694
+ normalized.phase_transition_request = nextPhase;
695
+ delete normalized.needs_human_reason;
696
+ }
697
+ }
698
+ }
699
+
700
+ // ── Rule 4: infer missing lifecycle signal for completed turns ────────
701
+ if (
702
+ isKnownPhase &&
703
+ isReviewOnly &&
704
+ normalized.status === 'completed' &&
705
+ normalized.run_completion_request == null &&
706
+ !normalized.phase_transition_request
707
+ ) {
708
+ if (isTerminalPhase) {
709
+ normalized.run_completion_request = true;
710
+ corrections.push(
711
+ `run_completion_request: inferred true for completed terminal phase "${currentPhase}"`
712
+ );
713
+ } else if (nextPhase) {
714
+ normalized.phase_transition_request = nextPhase;
715
+ corrections.push(
716
+ `phase_transition_request: inferred next phase "${nextPhase}" for completed phase "${currentPhase}"`
717
+ );
718
+ }
719
+ }
720
+
721
+ // ── Rule 5: correct invalid or non-forward lifecycle requests ─────────
722
+ if (
723
+ isKnownPhase &&
724
+ isReviewOnly &&
725
+ normalized.status === 'completed' &&
726
+ typeof normalized.phase_transition_request === 'string' &&
727
+ !normalized.run_completion_request
728
+ ) {
729
+ const requested = normalized.phase_transition_request;
730
+ const requestedIndex = phaseNames.indexOf(requested);
731
+ const invalidPhase = !(requested in (routing || {}));
732
+ const notForward = requestedIndex >= 0 && requestedIndex <= currentPhaseIndex;
733
+
734
+ if (invalidPhase || notForward) {
735
+ if (nextPhase) {
736
+ normalized.phase_transition_request = nextPhase;
737
+ corrections.push(
738
+ `phase_transition_request: corrected "${requested}" to forward phase "${nextPhase}"`
739
+ );
740
+ } else if (isTerminalPhase) {
741
+ normalized.phase_transition_request = null;
742
+ normalized.run_completion_request = true;
743
+ corrections.push(
744
+ `phase_transition_request: corrected terminal/non-forward "${requested}" to run_completion_request: true`
745
+ );
669
746
  }
670
747
  }
671
748
  }
672
749
 
750
+ // ── Rule 6: repair routing-illegal next-role signals only when safe ───
751
+ if (isReviewOnly && normalized.run_completion_request === true && !normalized.phase_transition_request) {
752
+ if (normalized.proposed_next_role !== 'human') {
753
+ normalized.proposed_next_role = 'human';
754
+ corrections.push('proposed_next_role: corrected to "human" for run completion');
755
+ }
756
+ } else if (
757
+ isKnownPhase &&
758
+ isReviewOnly &&
759
+ normalized.status === 'completed' &&
760
+ typeof normalized.proposed_next_role === 'string' &&
761
+ normalized.proposed_next_role !== 'human' &&
762
+ allowedNextRoles.length > 0 &&
763
+ !allowedNextRoles.includes(normalized.proposed_next_role)
764
+ ) {
765
+ const fallback = pickAllowedRoleFallback();
766
+ if (fallback) {
767
+ corrections.push(
768
+ `proposed_next_role: corrected "${normalized.proposed_next_role}" to allowed role "${fallback}"`
769
+ );
770
+ normalized.proposed_next_role = fallback;
771
+ }
772
+ }
773
+
673
774
  return { normalized, corrections };
674
775
  }
675
776