agentxchain 2.155.50 → 2.155.52
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
|
@@ -489,7 +489,7 @@ function renderPrompt(role, roleId, turn, state, config, root) {
|
|
|
489
489
|
lines.push('### Field Rules');
|
|
490
490
|
lines.push('');
|
|
491
491
|
lines.push('- `schema_version`: always `"1.0"`');
|
|
492
|
-
lines.push('- `run_id`, `turn_id`, `role`, `runtime_id`: must match the values above exactly');
|
|
492
|
+
lines.push('- `run_id`, `turn_id`, `role`, `runtime_id`: must match the current assignment values above exactly. Do NOT copy `run_id` from old reports, history entries, previous dispatch bundles, or retained staging JSON.');
|
|
493
493
|
lines.push('- `status`: one of `completed`, `blocked`, `needs_human`, `failed`. Do NOT use `complete`, `success`, `done`, or any other synonym — use the exact enum value `completed`.');
|
|
494
494
|
lines.push('- `decisions`: REQUIRED array. Use `[]` when no new decisions were made; do not omit the field.');
|
|
495
495
|
lines.push('- `objections`: REQUIRED array. Use `[]` when no objections are raised; review_only roles must include at least one objection.');
|
|
@@ -523,6 +523,7 @@ function renderPrompt(role, roleId, turn, state, config, root) {
|
|
|
523
523
|
if (phaseNames.length > 0) {
|
|
524
524
|
lines.push(`- \`phase_transition_request\`: set to a **phase name** when gate requirements are met, or \`null\`. Valid phases: ${phaseNames.map((p) => `\`"${p}"\``).join(', ')}`);
|
|
525
525
|
lines.push('- **Do NOT use exit gate names** (e.g., `planning_signoff`, `implementation_complete`, `qa_ship_verdict`) as `phase_transition_request` values — those are gate identifiers, not phase names');
|
|
526
|
+
lines.push('- **Do NOT skip ahead to later phases**. Use only the immediate next phase named below for the current phase.');
|
|
526
527
|
} else {
|
|
527
528
|
lines.push('- `phase_transition_request`: set to next phase name when gate requirements are met, or `null`');
|
|
528
529
|
}
|
|
@@ -78,6 +78,9 @@ export function validateStagedTurnResult(root, state, config, opts = {}) {
|
|
|
78
78
|
const normContext = {};
|
|
79
79
|
if (state) {
|
|
80
80
|
normContext.phase = state.phase;
|
|
81
|
+
if (state.run_id) {
|
|
82
|
+
normContext.runId = state.run_id;
|
|
83
|
+
}
|
|
81
84
|
// Prefer active_turns (the persisted schema field); fall back to the
|
|
82
85
|
// current_turn compatibility alias for callers that pass a state shape
|
|
83
86
|
// built outside loadProjectState() (e.g. raw fixtures). Both surfaces are
|
|
@@ -85,6 +88,12 @@ export function validateStagedTurnResult(root, state, config, opts = {}) {
|
|
|
85
88
|
if (activeTurn) {
|
|
86
89
|
const roleKey = activeTurn.assigned_role || activeTurn.role;
|
|
87
90
|
normContext.assignedRole = roleKey;
|
|
91
|
+
if (activeTurn.turn_id) {
|
|
92
|
+
normContext.turnId = activeTurn.turn_id;
|
|
93
|
+
}
|
|
94
|
+
if (activeTurn.run_id) {
|
|
95
|
+
normContext.activeTurnRunId = activeTurn.run_id;
|
|
96
|
+
}
|
|
88
97
|
if (activeTurn.runtime_id) {
|
|
89
98
|
normContext.runtimeId = activeTurn.runtime_id;
|
|
90
99
|
}
|
|
@@ -1006,6 +1015,29 @@ export function normalizeTurnResult(tr, config, context = {}) {
|
|
|
1006
1015
|
|
|
1007
1016
|
const normalized = { ...tr };
|
|
1008
1017
|
|
|
1018
|
+
// ── BUG-97: recover stale run_id for retained active turns ────────────
|
|
1019
|
+
// run_id is state-owned identity. A staged result may safely inherit it
|
|
1020
|
+
// only when the staged turn_id proves it belongs to the active retained
|
|
1021
|
+
// turn and the active-turn/state run identity is coherent.
|
|
1022
|
+
const stagedTurnMatchesActive = Boolean(
|
|
1023
|
+
context.runId
|
|
1024
|
+
&& context.turnId
|
|
1025
|
+
&& typeof normalized.turn_id === 'string'
|
|
1026
|
+
&& normalized.turn_id === context.turnId
|
|
1027
|
+
);
|
|
1028
|
+
const activeTurnRunIdCoherent = !context.activeTurnRunId || context.activeTurnRunId === context.runId;
|
|
1029
|
+
const stagedRunId = typeof normalized.run_id === 'string' ? normalized.run_id.trim() : '';
|
|
1030
|
+
if (stagedTurnMatchesActive && activeTurnRunIdCoherent && stagedRunId !== context.runId) {
|
|
1031
|
+
corrections.push(`run_id: rewritten from "${stagedRunId || '(missing)'}" to active state run_id "${context.runId}"`);
|
|
1032
|
+
normalizationEvents.push({
|
|
1033
|
+
field: 'run_id',
|
|
1034
|
+
original_value: normalized.run_id ?? null,
|
|
1035
|
+
normalized_value: context.runId,
|
|
1036
|
+
rationale: 'run_id_rewritten_from_active_turn_context',
|
|
1037
|
+
});
|
|
1038
|
+
normalized.run_id = context.runId;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1009
1041
|
// ── BUG-95: rename synonym fields before variable computation ────────
|
|
1010
1042
|
// files_modified is an unambiguous synonym for files_changed.
|
|
1011
1043
|
if (!('files_changed' in normalized) && Array.isArray(normalized.files_modified)) {
|
|
@@ -1358,6 +1390,9 @@ export function normalizeTurnResult(tr, config, context = {}) {
|
|
|
1358
1390
|
if (allowedNextRoles.length === 0) return null;
|
|
1359
1391
|
return allowedNextRoles.find((role) => role !== assignedRole) || allowedNextRoles[0] || null;
|
|
1360
1392
|
};
|
|
1393
|
+
const nextPhaseEntryRole = nextPhase && routing?.[nextPhase]?.entry_role
|
|
1394
|
+
? routing[nextPhase].entry_role
|
|
1395
|
+
: null;
|
|
1361
1396
|
|
|
1362
1397
|
// ── Rule 0: infer missing status only when intent is unambiguous ──────
|
|
1363
1398
|
if (!('status' in normalized)) {
|
|
@@ -1505,6 +1540,54 @@ export function normalizeTurnResult(tr, config, context = {}) {
|
|
|
1505
1540
|
}
|
|
1506
1541
|
|
|
1507
1542
|
// ── Rule 5: correct invalid or non-forward lifecycle requests ─────────
|
|
1543
|
+
if (
|
|
1544
|
+
isKnownPhase &&
|
|
1545
|
+
!isTerminalPhase &&
|
|
1546
|
+
!isReviewOnly &&
|
|
1547
|
+
normalized.status === 'completed' &&
|
|
1548
|
+
typeof normalized.phase_transition_request === 'string' &&
|
|
1549
|
+
!normalized.run_completion_request
|
|
1550
|
+
) {
|
|
1551
|
+
const requested = normalized.phase_transition_request;
|
|
1552
|
+
const requestedIndex = phaseNames.indexOf(requested);
|
|
1553
|
+
const skipsImmediateNextPhase = requestedIndex > currentPhaseIndex + 1;
|
|
1554
|
+
|
|
1555
|
+
if (skipsImmediateNextPhase && nextPhase) {
|
|
1556
|
+
normalized.phase_transition_request = nextPhase;
|
|
1557
|
+
corrections.push(
|
|
1558
|
+
`phase_transition_request: corrected skip-forward "${requested}" to immediate next phase "${nextPhase}"`
|
|
1559
|
+
);
|
|
1560
|
+
normalizationEvents.push({
|
|
1561
|
+
field: 'phase_transition_request',
|
|
1562
|
+
original_value: requested,
|
|
1563
|
+
normalized_value: nextPhase,
|
|
1564
|
+
rationale: 'skip_forward_phase_corrected_to_next_phase',
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
const proposedRoleIsStalePhase = normalized.proposed_next_role === requested;
|
|
1568
|
+
const proposedRoleIsRoutingIllegal = typeof normalized.proposed_next_role === 'string'
|
|
1569
|
+
&& normalized.proposed_next_role !== 'human'
|
|
1570
|
+
&& allowedNextRoles.length > 0
|
|
1571
|
+
&& !allowedNextRoles.includes(normalized.proposed_next_role);
|
|
1572
|
+
if (
|
|
1573
|
+
nextPhaseEntryRole &&
|
|
1574
|
+
allowedNextRoles.includes(nextPhaseEntryRole) &&
|
|
1575
|
+
(proposedRoleIsStalePhase || proposedRoleIsRoutingIllegal)
|
|
1576
|
+
) {
|
|
1577
|
+
corrections.push(
|
|
1578
|
+
`proposed_next_role: corrected "${normalized.proposed_next_role}" to entry role "${nextPhaseEntryRole}" for corrected phase "${nextPhase}"`
|
|
1579
|
+
);
|
|
1580
|
+
normalizationEvents.push({
|
|
1581
|
+
field: 'proposed_next_role',
|
|
1582
|
+
original_value: normalized.proposed_next_role,
|
|
1583
|
+
normalized_value: nextPhaseEntryRole,
|
|
1584
|
+
rationale: 'aligned_to_corrected_phase_entry_role',
|
|
1585
|
+
});
|
|
1586
|
+
normalized.proposed_next_role = nextPhaseEntryRole;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1508
1591
|
if (
|
|
1509
1592
|
isKnownPhase &&
|
|
1510
1593
|
isReviewOnly &&
|