agentxchain 2.155.29 → 2.155.30
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/bin/agentxchain.js +1 -0
- package/package.json +1 -1
- package/src/commands/accept-turn.js +5 -0
- package/src/commands/init.js +1 -0
- package/src/lib/dispatch-bundle.js +1 -0
- package/src/lib/governed-state.js +20 -1
- package/src/lib/run-events.js +1 -0
- package/src/lib/turn-result-validator.js +42 -4
package/bin/agentxchain.js
CHANGED
|
@@ -708,6 +708,7 @@ program
|
|
|
708
708
|
.option('--checkpoint', 'Checkpoint the accepted turn to git immediately after acceptance')
|
|
709
709
|
.option('--resolution <mode>', 'Conflict resolution mode for conflicted turns (standard, human_merge)', 'standard')
|
|
710
710
|
.option('--normalize-artifact-type <type>', 'Normalize an empty workspace artifact to a safe artifact type before acceptance (currently: review)')
|
|
711
|
+
.option('--normalize-staged-result', 'Run known-safe staged-result normalization before acceptance')
|
|
711
712
|
.action(acceptTurnCommand);
|
|
712
713
|
|
|
713
714
|
program
|
package/package.json
CHANGED
|
@@ -23,6 +23,7 @@ export async function acceptTurnCommand(opts = {}) {
|
|
|
23
23
|
turnId: opts.turn,
|
|
24
24
|
resolutionMode: opts.resolution || 'standard',
|
|
25
25
|
normalizeArtifactType: opts.normalizeArtifactType || null,
|
|
26
|
+
normalizeStagedResult: Boolean(opts.normalizeStagedResult),
|
|
26
27
|
});
|
|
27
28
|
if (!result.ok) {
|
|
28
29
|
if (result.error_code === 'policy_escalation' || result.error_code === 'policy_violation') {
|
|
@@ -137,6 +138,10 @@ export async function acceptTurnCommand(opts = {}) {
|
|
|
137
138
|
console.log(` ${chalk.dim('Reason:')} ${errorClass}`);
|
|
138
139
|
console.log(` ${chalk.dim('Owner:')} human`);
|
|
139
140
|
console.log(` ${chalk.dim('Action:')} Fix staged result and rerun agentxchain accept-turn, or reject with agentxchain reject-turn --reason "..."`);
|
|
141
|
+
if (result.validation?.errors?.some((err) => /objections\[\d+\]\.statement/.test(err))) {
|
|
142
|
+
const retainedTurnId = opts.turn || result.state?.current_turn?.turn_id || '(turn_id)';
|
|
143
|
+
console.log(` ${chalk.dim('Recovery:')} agentxchain accept-turn --turn ${retainedTurnId} --normalize-staged-result`);
|
|
144
|
+
}
|
|
140
145
|
console.log(` ${chalk.dim('Turn:')} retained`);
|
|
141
146
|
if (result.validation?.errors?.length) {
|
|
142
147
|
for (const err of result.validation.errors) {
|
package/src/commands/init.js
CHANGED
|
@@ -546,6 +546,7 @@ You are the **${role.title}** on this project.
|
|
|
546
546
|
3. Write your structured turn result to the turn-scoped staging path printed by the orchestrator (\`.agentxchain/staging/<turn_id>/turn-result.json\`).
|
|
547
547
|
4. If you make zero repo file edits, set \`artifact.type: "review"\` and \`files_changed: []\`.
|
|
548
548
|
5. Only set \`artifact.type: "workspace"\` when you actually modified repo files and listed every changed path in \`files_changed\`.
|
|
549
|
+
6. Every objection must include a non-empty \`statement\`; do not use \`summary\` or \`detail\` as a substitute.
|
|
549
550
|
|
|
550
551
|
## File Access
|
|
551
552
|
|
|
@@ -502,6 +502,7 @@ function renderPrompt(role, roleId, turn, state, config, root) {
|
|
|
502
502
|
lines.push('- `artifact.type`: one of `workspace`, `patch`, `commit`, `review`');
|
|
503
503
|
lines.push('- If you make zero repo file edits, set `artifact.type` to `"review"` and `files_changed` to `[]`.');
|
|
504
504
|
lines.push('- Only set `artifact.type` to `"workspace"` when you actually modified repo files and listed every changed path in `files_changed`.');
|
|
505
|
+
lines.push('- Every `objections[]` item must include a non-empty `statement`; do not use `summary` or `detail` as a substitute.');
|
|
505
506
|
lines.push('- `proposed_next_role`: must be in allowed_next_roles for current phase, or `human`');
|
|
506
507
|
if (role.write_authority === 'review_only') {
|
|
507
508
|
lines.push('- `objections`: **must be non-empty** (challenge requirement for review_only roles)');
|
|
@@ -4315,7 +4315,10 @@ function _acceptGovernedTurnLocked(root, config, opts) {
|
|
|
4315
4315
|
}
|
|
4316
4316
|
|
|
4317
4317
|
if (!validation.ok) {
|
|
4318
|
-
const
|
|
4318
|
+
const recoveryHint = validation.errors.some((error) => /objections\[\d+\]\.statement/.test(error))
|
|
4319
|
+
? ` Recovery: agentxchain accept-turn --turn ${currentTurn.turn_id} --normalize-staged-result`
|
|
4320
|
+
: '';
|
|
4321
|
+
const failError = `Validation failed at stage ${validation.stage}: ${validation.errors.join('; ')}${recoveryHint}`;
|
|
4319
4322
|
transitionToFailedAcceptance(root, state, currentTurn, failError, {
|
|
4320
4323
|
error_code: 'validation_failed',
|
|
4321
4324
|
stage: validation.stage,
|
|
@@ -4345,6 +4348,22 @@ function _acceptGovernedTurnLocked(root, config, opts) {
|
|
|
4345
4348
|
},
|
|
4346
4349
|
});
|
|
4347
4350
|
}
|
|
4351
|
+
for (const event of Array.isArray(validation.normalization_events) ? validation.normalization_events : []) {
|
|
4352
|
+
emitRunEvent(root, 'staged_result_auto_normalized', {
|
|
4353
|
+
run_id: state.run_id,
|
|
4354
|
+
phase: state.phase,
|
|
4355
|
+
status: state.status,
|
|
4356
|
+
turn: { turn_id: currentTurn.turn_id, role_id: currentTurn.assigned_role },
|
|
4357
|
+
intent_id: currentTurn.intake_context?.intent_id || null,
|
|
4358
|
+
payload: {
|
|
4359
|
+
field: event.field,
|
|
4360
|
+
original_value: event.original_value,
|
|
4361
|
+
normalized_value: event.normalized_value,
|
|
4362
|
+
rationale: event.rationale,
|
|
4363
|
+
staging_path: resolvedStagingPath,
|
|
4364
|
+
},
|
|
4365
|
+
});
|
|
4366
|
+
}
|
|
4348
4367
|
|
|
4349
4368
|
const rawTurnResult = validation.turnResult;
|
|
4350
4369
|
const verificationProducedFiles = normalizeVerificationProducedFiles(rawTurnResult.verification);
|
package/src/lib/run-events.js
CHANGED
|
@@ -94,7 +94,7 @@ export function validateStagedTurnResult(root, state, config, opts = {}) {
|
|
|
94
94
|
if (opts.normalizeArtifactType === 'review') {
|
|
95
95
|
normContext.forceReviewArtifact = true;
|
|
96
96
|
}
|
|
97
|
-
const { normalized, corrections } = normalizeTurnResult(turnResult, config, normContext);
|
|
97
|
+
const { normalized, corrections, normalizationEvents } = normalizeTurnResult(turnResult, config, normContext);
|
|
98
98
|
turnResult = normalized;
|
|
99
99
|
const sidecarResult = maybeAttachIdleExpansionSidecar(
|
|
100
100
|
root,
|
|
@@ -159,6 +159,7 @@ export function validateStagedTurnResult(root, state, config, opts = {}) {
|
|
|
159
159
|
warnings: allWarnings,
|
|
160
160
|
turnResult,
|
|
161
161
|
normalizations: corrections,
|
|
162
|
+
normalization_events: normalizationEvents,
|
|
162
163
|
};
|
|
163
164
|
}
|
|
164
165
|
|
|
@@ -995,8 +996,9 @@ function validateProtocol(tr, state, config) {
|
|
|
995
996
|
*/
|
|
996
997
|
export function normalizeTurnResult(tr, config, context = {}) {
|
|
997
998
|
const corrections = [];
|
|
999
|
+
const normalizationEvents = [];
|
|
998
1000
|
if (tr === null || typeof tr !== 'object' || Array.isArray(tr)) {
|
|
999
|
-
return { normalized: tr, corrections };
|
|
1001
|
+
return { normalized: tr, corrections, normalizationEvents };
|
|
1000
1002
|
}
|
|
1001
1003
|
|
|
1002
1004
|
const normalized = { ...tr };
|
|
@@ -1023,7 +1025,7 @@ export function normalizeTurnResult(tr, config, context = {}) {
|
|
|
1023
1025
|
&& !Array.isArray(normalized.artifact)
|
|
1024
1026
|
&& normalized.artifact.type === 'workspace'
|
|
1025
1027
|
&& filesChangedIsEmpty
|
|
1026
|
-
&& (context.forceReviewArtifact ||
|
|
1028
|
+
&& (context.forceReviewArtifact || hasExplicitNoEditLifecycleSignal)
|
|
1027
1029
|
) {
|
|
1028
1030
|
normalized.artifact = {
|
|
1029
1031
|
...normalized.artifact,
|
|
@@ -1033,6 +1035,42 @@ export function normalizeTurnResult(tr, config, context = {}) {
|
|
|
1033
1035
|
: normalized.artifact.ref ?? null,
|
|
1034
1036
|
};
|
|
1035
1037
|
corrections.push('artifact.type: auto-normalized empty workspace artifact to review because files_changed is empty and no repo mutation was declared');
|
|
1038
|
+
normalizationEvents.push({
|
|
1039
|
+
field: 'artifact.type',
|
|
1040
|
+
original_value: 'workspace',
|
|
1041
|
+
normalized_value: 'review',
|
|
1042
|
+
rationale: 'empty_files_changed_no_repo_mutation_declared',
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
if (Array.isArray(normalized.objections)) {
|
|
1047
|
+
normalized.objections = normalized.objections.map((objection, index) => {
|
|
1048
|
+
if (objection === null || typeof objection !== 'object' || Array.isArray(objection)) {
|
|
1049
|
+
return objection;
|
|
1050
|
+
}
|
|
1051
|
+
const statement = typeof objection.statement === 'string' ? objection.statement.trim() : '';
|
|
1052
|
+
if (statement) {
|
|
1053
|
+
return objection;
|
|
1054
|
+
}
|
|
1055
|
+
const summary = typeof objection.summary === 'string' ? objection.summary.trim() : '';
|
|
1056
|
+
const detail = typeof objection.detail === 'string' ? objection.detail.trim() : '';
|
|
1057
|
+
const sourceField = summary ? 'summary' : detail ? 'detail' : null;
|
|
1058
|
+
const sourceValue = summary || detail;
|
|
1059
|
+
if (!sourceField) {
|
|
1060
|
+
return objection;
|
|
1061
|
+
}
|
|
1062
|
+
corrections.push(`objections[${index}].statement: copied from ${sourceField}`);
|
|
1063
|
+
normalizationEvents.push({
|
|
1064
|
+
field: `objections[${index}].statement`,
|
|
1065
|
+
original_value: objection.statement ?? null,
|
|
1066
|
+
normalized_value: sourceValue,
|
|
1067
|
+
rationale: `copied_from_${sourceField}`,
|
|
1068
|
+
});
|
|
1069
|
+
return {
|
|
1070
|
+
...objection,
|
|
1071
|
+
statement: sourceValue,
|
|
1072
|
+
};
|
|
1073
|
+
});
|
|
1036
1074
|
}
|
|
1037
1075
|
|
|
1038
1076
|
const pickAllowedRoleFallback = () => {
|
|
@@ -1238,7 +1276,7 @@ export function normalizeTurnResult(tr, config, context = {}) {
|
|
|
1238
1276
|
}
|
|
1239
1277
|
}
|
|
1240
1278
|
|
|
1241
|
-
return { normalized, corrections };
|
|
1279
|
+
return { normalized, corrections, normalizationEvents };
|
|
1242
1280
|
}
|
|
1243
1281
|
|
|
1244
1282
|
function normalizeIdleExpansionMutualExclusionSentinel(result) {
|