agentxchain 2.155.36 → 2.155.38
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
package/scripts/release-bump.sh
CHANGED
|
@@ -89,6 +89,7 @@ ALLOWED_RELEASE_PATHS=(
|
|
|
89
89
|
"website-v2/docs/getting-started.mdx"
|
|
90
90
|
"website-v2/docs/quickstart.mdx"
|
|
91
91
|
"website-v2/docs/five-minute-tutorial.mdx"
|
|
92
|
+
"website-v2/docs/releases/v2-147-0.mdx"
|
|
92
93
|
"cli/test/hn-launch-surface-content.test.js"
|
|
93
94
|
"cli/homebrew/agentxchain.rb"
|
|
94
95
|
"cli/homebrew/README.md"
|
|
@@ -499,6 +499,7 @@ function renderPrompt(role, roleId, turn, state, config, root) {
|
|
|
499
499
|
lines.push('- `verification.status`: one of `pass`, `fail`, `skipped`');
|
|
500
500
|
lines.push('- `verification.status: "pass"` is valid only when every `verification.machine_evidence[].exit_code` is `0`');
|
|
501
501
|
lines.push('- Expected-failure checks must be wrapped in a verifier that exits `0` when the failure occurs as expected; do not list raw non-zero negative-case commands on a passing turn');
|
|
502
|
+
lines.push('- If verification commands produce side-effect files (e.g., `.tusq/plan.json`, `coverage/`, `.cache/`), declare each in `verification.produced_files` with `disposition: "ignore"` (temporary output to clean up) or `disposition: "artifact"` (output to checkpoint as a turn deliverable). Undeclared dirty files with declared verification will be auto-cleaned but declaring them is preferred.');
|
|
502
503
|
lines.push('- `artifact.type`: one of `workspace`, `patch`, `commit`, `review`');
|
|
503
504
|
lines.push('- If you make zero repo file edits, set `artifact.type` to `"review"` and `files_changed` to `[]`.');
|
|
504
505
|
lines.push('- Only set `artifact.type` to `"workspace"` when you actually modified repo files and listed every changed path in `files_changed`.');
|
|
@@ -98,6 +98,49 @@ function verifyFileEntry(relPath, entry, errors) {
|
|
|
98
98
|
addError(errors, path, 'sha256 must be a 64-character lowercase hex digest');
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
if (entry.content_base64 === null) {
|
|
102
|
+
const isTruncated = entry.truncated === true;
|
|
103
|
+
const isSkipped = entry.content_base64_skipped === true;
|
|
104
|
+
if (!isTruncated && !isSkipped) {
|
|
105
|
+
addError(errors, path, 'content_base64 may be null only when truncated or content_base64_skipped is true');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (isTruncated) {
|
|
109
|
+
if (entry.format !== 'jsonl') {
|
|
110
|
+
addError(errors, path, 'truncated file entries must use jsonl format');
|
|
111
|
+
}
|
|
112
|
+
if (!Array.isArray(entry.data)) {
|
|
113
|
+
addError(errors, path, 'truncated JSONL data must be an array');
|
|
114
|
+
}
|
|
115
|
+
if (!Number.isInteger(entry.total_entries) || entry.total_entries < 0) {
|
|
116
|
+
addError(errors, path, 'total_entries must be a non-negative integer when truncated');
|
|
117
|
+
}
|
|
118
|
+
if (!Number.isInteger(entry.retained_entries) || entry.retained_entries < 0) {
|
|
119
|
+
addError(errors, path, 'retained_entries must be a non-negative integer when truncated');
|
|
120
|
+
}
|
|
121
|
+
if (Array.isArray(entry.data) && entry.retained_entries !== entry.data.length) {
|
|
122
|
+
addError(errors, path, 'retained_entries must match truncated data length');
|
|
123
|
+
}
|
|
124
|
+
if (
|
|
125
|
+
Number.isInteger(entry.total_entries)
|
|
126
|
+
&& Number.isInteger(entry.retained_entries)
|
|
127
|
+
&& entry.retained_entries > entry.total_entries
|
|
128
|
+
) {
|
|
129
|
+
addError(errors, path, 'retained_entries must not exceed total_entries');
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (entry.format === 'jsonl' && !Array.isArray(entry.data)) {
|
|
134
|
+
addError(errors, path, 'skipped JSONL data must be an array');
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (entry.format === 'text' && typeof entry.data !== 'string') {
|
|
138
|
+
addError(errors, path, 'skipped text data must be a string');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
101
144
|
if (typeof entry.content_base64 !== 'string') {
|
|
102
145
|
addError(errors, path, 'content_base64 must be a string');
|
|
103
146
|
return;
|
|
@@ -4531,39 +4531,97 @@ function _acceptGovernedTurnLocked(root, config, opts) {
|
|
|
4531
4531
|
&& verification.machine_evidence.some((e) => e && typeof e === 'object' && typeof e.command === 'string' && e.command.trim().length > 0);
|
|
4532
4532
|
const verificationWasDeclared = declaredVerificationCommands || declaredMachineEvidence;
|
|
4533
4533
|
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4534
|
+
// ── BUG-87: Auto-normalize undeclared verification outputs ──────────
|
|
4535
|
+
// When verification was declared and unexpected dirty files exist,
|
|
4536
|
+
// attempt to clean them up as ignored verification outputs before
|
|
4537
|
+
// hard-erroring. This covers tool-generated side-effect files (e.g.,
|
|
4538
|
+
// .tusq/plan.json, coverage/) that the agent failed to declare in
|
|
4539
|
+
// verification.produced_files. cleanupIgnoredVerificationFiles already
|
|
4540
|
+
// respects the dispatch baseline: only files NOT dirty at dispatch
|
|
4541
|
+
// time are cleaned; pre-existing dirty files are left untouched.
|
|
4542
|
+
if (verificationWasDeclared && Array.isArray(dirtyParity.unexpected_dirty_files) && dirtyParity.unexpected_dirty_files.length > 0) {
|
|
4543
|
+
const autoClassifyAttempt = cleanupIgnoredVerificationFiles({
|
|
4544
|
+
root,
|
|
4545
|
+
baseline,
|
|
4546
|
+
ignoredFiles: dirtyParity.unexpected_dirty_files,
|
|
4547
|
+
});
|
|
4548
|
+
if (autoClassifyAttempt.ok) {
|
|
4549
|
+
// Re-check dirty parity after cleanup
|
|
4550
|
+
const recheckParity = detectDirtyFilesOutsideAllowed(
|
|
4551
|
+
root,
|
|
4552
|
+
writeAuthority,
|
|
4553
|
+
[
|
|
4554
|
+
...(turnResult.files_changed || []),
|
|
4555
|
+
...concurrentAllowedDirtyFiles,
|
|
4556
|
+
...uncheckpointedPriorFiles,
|
|
4557
|
+
],
|
|
4558
|
+
);
|
|
4559
|
+
if (recheckParity.clean) {
|
|
4560
|
+
const autoClassifiedFiles = dirtyParity.unexpected_dirty_files;
|
|
4561
|
+
// Auto-normalization succeeded — emit audit event and continue
|
|
4562
|
+
emitRunEvent(root, 'verification_output_auto_normalized', {
|
|
4563
|
+
run_id: state.run_id,
|
|
4564
|
+
phase: state.phase,
|
|
4565
|
+
status: state.status,
|
|
4566
|
+
turn: { turn_id: currentTurn.turn_id, role_id: currentTurn.assigned_role },
|
|
4567
|
+
intent_id: currentTurn.intake_context?.intent_id || null,
|
|
4568
|
+
payload: {
|
|
4569
|
+
auto_classified_files: autoClassifiedFiles,
|
|
4570
|
+
restored_files: autoClassifyAttempt.restored_files || [],
|
|
4571
|
+
disposition: 'ignore',
|
|
4572
|
+
rationale: 'undeclared_verification_output_auto_normalized',
|
|
4573
|
+
},
|
|
4574
|
+
});
|
|
4575
|
+
// Remove auto-classified files from the observation so
|
|
4576
|
+
// compareDeclaredVsObserved does not re-flag them as undeclared.
|
|
4577
|
+
const autoSet = new Set(autoClassifiedFiles);
|
|
4578
|
+
if (Array.isArray(observation.files_changed)) {
|
|
4579
|
+
observation.files_changed = observation.files_changed.filter(
|
|
4580
|
+
(f) => !autoSet.has(f),
|
|
4581
|
+
);
|
|
4582
|
+
}
|
|
4583
|
+
// Fall through to the rest of acceptance (skip the error block)
|
|
4584
|
+
dirtyParity.clean = true;
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4544
4587
|
}
|
|
4588
|
+
// ── End BUG-87 ──────────────────────────────────────────────────────
|
|
4589
|
+
|
|
4590
|
+
if (!dirtyParity.clean) {
|
|
4591
|
+
let failureReason = dirtyParity.reason;
|
|
4592
|
+
let failureErrorCode = 'artifact_dirty_tree_mismatch';
|
|
4593
|
+
if (verificationWasDeclared) {
|
|
4594
|
+
failureErrorCode = 'undeclared_verification_outputs';
|
|
4595
|
+
const undeclared = Array.isArray(dirtyParity.unexpected_dirty_files)
|
|
4596
|
+
? dirtyParity.unexpected_dirty_files
|
|
4597
|
+
: [];
|
|
4598
|
+
const listForMessage = undeclared.slice(0, 5).join(', ')
|
|
4599
|
+
+ (undeclared.length > 5 ? '...' : '');
|
|
4600
|
+
failureReason = `Verification was declared (commands or machine_evidence), but these files are dirty and not classified: ${listForMessage}. Classify each under verification.produced_files with disposition "ignore" (the file should be cleaned up after replay) or "artifact" (the file should be checkpointed as part of the turn), OR add it to files_changed if it is a core turn mutation. Acceptance cannot proceed until the declared contract matches the working tree.`;
|
|
4601
|
+
}
|
|
4545
4602
|
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
stage: 'artifact_observation',
|
|
4549
|
-
extra: {
|
|
4550
|
-
unexpected_dirty_files: dirtyParity.unexpected_dirty_files,
|
|
4551
|
-
dirty_files: dirtyParity.dirty_files,
|
|
4552
|
-
},
|
|
4553
|
-
});
|
|
4554
|
-
return {
|
|
4555
|
-
ok: false,
|
|
4556
|
-
error: failureReason,
|
|
4557
|
-
error_code: failureErrorCode,
|
|
4558
|
-
validation: {
|
|
4559
|
-
...validation,
|
|
4560
|
-
ok: false,
|
|
4603
|
+
transitionToFailedAcceptance(root, state, currentTurn, failureReason, {
|
|
4604
|
+
error_code: failureErrorCode,
|
|
4561
4605
|
stage: 'artifact_observation',
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4606
|
+
extra: {
|
|
4607
|
+
unexpected_dirty_files: dirtyParity.unexpected_dirty_files,
|
|
4608
|
+
dirty_files: dirtyParity.dirty_files,
|
|
4609
|
+
},
|
|
4610
|
+
});
|
|
4611
|
+
return {
|
|
4612
|
+
ok: false,
|
|
4613
|
+
error: failureReason,
|
|
4614
|
+
error_code: failureErrorCode,
|
|
4615
|
+
validation: {
|
|
4616
|
+
...validation,
|
|
4617
|
+
ok: false,
|
|
4618
|
+
stage: 'artifact_observation',
|
|
4619
|
+
error_class: 'artifact_error',
|
|
4620
|
+
errors: [failureReason],
|
|
4621
|
+
warnings: validation.warnings,
|
|
4622
|
+
},
|
|
4623
|
+
};
|
|
4624
|
+
}
|
|
4567
4625
|
}
|
|
4568
4626
|
|
|
4569
4627
|
const diffComparison = compareDeclaredVsObserved(
|