agentxchain 2.155.38 → 2.155.39
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 +1 -1
- package/src/commands/run.js +31 -5
- package/src/lib/export-verifier.js +29 -21
- package/src/lib/export.js +41 -1
package/package.json
CHANGED
package/src/commands/run.js
CHANGED
|
@@ -673,26 +673,52 @@ export async function executeGovernedRun(context, opts = {}) {
|
|
|
673
673
|
const reportsDir = join(root, '.agentxchain', 'reports');
|
|
674
674
|
mkdirSync(reportsDir, { recursive: true });
|
|
675
675
|
|
|
676
|
-
|
|
676
|
+
// BUG-88: two-attempt export with fallback to tighter bounds on string-length overflow
|
|
677
|
+
const defaultExportOpts = { maxJsonlEntries: 1000, maxBase64Bytes: 1024 * 1024, maxExportFiles: 500, maxTextDataBytes: 131072 };
|
|
678
|
+
const tightExportOpts = { maxJsonlEntries: 500, maxBase64Bytes: 65536, maxExportFiles: 200, maxTextDataBytes: 32768 };
|
|
679
|
+
|
|
680
|
+
let exportResult = buildRunExport(root, defaultExportOpts);
|
|
677
681
|
if (exportResult.ok) {
|
|
678
682
|
const runId = result.state.run_id || 'unknown';
|
|
679
683
|
const exportPath = join(reportsDir, `export-${runId}.json`);
|
|
684
|
+
let exportWriteOk = false;
|
|
685
|
+
let usedExport = exportResult.export;
|
|
680
686
|
|
|
681
|
-
// Write export JSON — compact format to avoid string-length overflow (BUG-84)
|
|
687
|
+
// Write export JSON — compact format to avoid string-length overflow (BUG-84/88)
|
|
682
688
|
try {
|
|
683
689
|
writeFileSync(exportPath, JSON.stringify(exportResult.export));
|
|
690
|
+
exportWriteOk = true;
|
|
684
691
|
} catch (exportWriteErr) {
|
|
685
|
-
|
|
692
|
+
// BUG-88: retry with tighter bounds on Invalid string length
|
|
693
|
+
if (/Invalid string length/i.test(exportWriteErr.message)) {
|
|
694
|
+
log(chalk.dim(` Export write exceeded string limit; retrying with tighter bounds...`));
|
|
695
|
+
const tightResult = buildRunExport(root, tightExportOpts);
|
|
696
|
+
if (tightResult.ok) {
|
|
697
|
+
try {
|
|
698
|
+
writeFileSync(exportPath, JSON.stringify(tightResult.export));
|
|
699
|
+
exportWriteOk = true;
|
|
700
|
+
usedExport = tightResult.export;
|
|
701
|
+
} catch (retryErr) {
|
|
702
|
+
log(chalk.dim(` Governance export write failed (tight bounds): ${retryErr.message}`));
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
} else {
|
|
706
|
+
log(chalk.dim(` Governance export write failed: ${exportWriteErr.message}`));
|
|
707
|
+
}
|
|
686
708
|
}
|
|
687
709
|
|
|
688
|
-
// Generate markdown report
|
|
710
|
+
// Generate markdown report from whichever export succeeded
|
|
689
711
|
try {
|
|
690
|
-
const
|
|
712
|
+
const reportInput = exportWriteOk ? exportPath : '(in-memory)';
|
|
713
|
+
const reportResult = buildGovernanceReport(usedExport, { input: reportInput });
|
|
691
714
|
const reportPath = join(reportsDir, `report-${runId}.md`);
|
|
692
715
|
writeFileSync(reportPath, formatGovernanceReportMarkdown(reportResult.report));
|
|
693
716
|
|
|
694
717
|
log('');
|
|
695
718
|
log(chalk.dim(` Governance report: .agentxchain/reports/report-${runId}.md`));
|
|
719
|
+
if (!exportWriteOk) {
|
|
720
|
+
log(chalk.dim(` Note: report generated from in-memory bounded export (disk write failed).`));
|
|
721
|
+
}
|
|
696
722
|
} catch (reportErr) {
|
|
697
723
|
log(chalk.dim(` Governance report failed: ${reportErr.message}`));
|
|
698
724
|
}
|
|
@@ -106,27 +106,35 @@ function verifyFileEntry(relPath, entry, errors) {
|
|
|
106
106
|
return;
|
|
107
107
|
}
|
|
108
108
|
if (isTruncated) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
109
|
+
// BUG-88: truncated entries may be JSONL (windowed) or text (size-capped)
|
|
110
|
+
if (entry.format === 'jsonl') {
|
|
111
|
+
if (!Array.isArray(entry.data)) {
|
|
112
|
+
addError(errors, path, 'truncated JSONL data must be an array');
|
|
113
|
+
}
|
|
114
|
+
if (!Number.isInteger(entry.total_entries) || entry.total_entries < 0) {
|
|
115
|
+
addError(errors, path, 'total_entries must be a non-negative integer when truncated');
|
|
116
|
+
}
|
|
117
|
+
if (!Number.isInteger(entry.retained_entries) || entry.retained_entries < 0) {
|
|
118
|
+
addError(errors, path, 'retained_entries must be a non-negative integer when truncated');
|
|
119
|
+
}
|
|
120
|
+
if (Array.isArray(entry.data) && entry.retained_entries !== entry.data.length) {
|
|
121
|
+
addError(errors, path, 'retained_entries must match truncated data length');
|
|
122
|
+
}
|
|
123
|
+
if (
|
|
124
|
+
Number.isInteger(entry.total_entries)
|
|
125
|
+
&& Number.isInteger(entry.retained_entries)
|
|
126
|
+
&& entry.retained_entries > entry.total_entries
|
|
127
|
+
) {
|
|
128
|
+
addError(errors, path, 'retained_entries must not exceed total_entries');
|
|
129
|
+
}
|
|
130
|
+
} else if (entry.format === 'text') {
|
|
131
|
+
if (typeof entry.data !== 'string') {
|
|
132
|
+
addError(errors, path, 'truncated text data must be a string');
|
|
133
|
+
}
|
|
134
|
+
} else if (entry.format === 'json') {
|
|
135
|
+
// Truncated JSON: data may be partial or null
|
|
136
|
+
} else {
|
|
137
|
+
addError(errors, path, `truncated file entries must use jsonl, text, or json format, got: ${entry.format}`);
|
|
130
138
|
}
|
|
131
139
|
return;
|
|
132
140
|
}
|
package/src/lib/export.js
CHANGED
|
@@ -182,6 +182,10 @@ function parseFile(root, relPath, opts = {}) {
|
|
|
182
182
|
totalEntries = parsed.totalEntries;
|
|
183
183
|
} else {
|
|
184
184
|
data = buffer.toString('utf8');
|
|
185
|
+
if (opts.maxTextDataBytes && typeof data === 'string' && data.length > opts.maxTextDataBytes) {
|
|
186
|
+
data = data.slice(0, opts.maxTextDataBytes);
|
|
187
|
+
truncated = true;
|
|
188
|
+
}
|
|
185
189
|
}
|
|
186
190
|
|
|
187
191
|
const skipBase64 = truncated || (opts.maxBase64Bytes && buffer.byteLength > opts.maxBase64Bytes);
|
|
@@ -464,9 +468,42 @@ export function buildRunExport(startDir = process.cwd(), exportOpts = {}) {
|
|
|
464
468
|
if (exportOpts.maxBase64Bytes) {
|
|
465
469
|
parseOpts.maxBase64Bytes = exportOpts.maxBase64Bytes;
|
|
466
470
|
}
|
|
471
|
+
if (exportOpts.maxTextDataBytes) {
|
|
472
|
+
parseOpts.maxTextDataBytes = exportOpts.maxTextDataBytes;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// BUG-88: apply maxExportFiles cap with priority ordering.
|
|
476
|
+
// Core governance files first, then dispatch/staging, then .planning/ last.
|
|
477
|
+
let boundedPaths = collectedPaths;
|
|
478
|
+
let exportFilesTruncated = false;
|
|
479
|
+
if (exportOpts.maxExportFiles && collectedPaths.length > exportOpts.maxExportFiles) {
|
|
480
|
+
const corePaths = [];
|
|
481
|
+
const runtimePaths = [];
|
|
482
|
+
const planningPaths = [];
|
|
483
|
+
for (const p of collectedPaths) {
|
|
484
|
+
if (p.startsWith('.planning/')) {
|
|
485
|
+
planningPaths.push(p);
|
|
486
|
+
} else if (p.startsWith('.agentxchain/dispatch/') || p.startsWith('.agentxchain/staging/') || p.startsWith('.agentxchain/transactions/')) {
|
|
487
|
+
runtimePaths.push(p);
|
|
488
|
+
} else {
|
|
489
|
+
corePaths.push(p);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const budget = exportOpts.maxExportFiles;
|
|
493
|
+
boundedPaths = corePaths.slice(0, budget);
|
|
494
|
+
const remaining = budget - boundedPaths.length;
|
|
495
|
+
if (remaining > 0) {
|
|
496
|
+
boundedPaths.push(...runtimePaths.slice(0, remaining));
|
|
497
|
+
const remaining2 = remaining - Math.min(runtimePaths.length, remaining);
|
|
498
|
+
if (remaining2 > 0) {
|
|
499
|
+
boundedPaths.push(...planningPaths.slice(0, remaining2));
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
exportFilesTruncated = true;
|
|
503
|
+
}
|
|
467
504
|
|
|
468
505
|
const files = {};
|
|
469
|
-
for (const relPath of
|
|
506
|
+
for (const relPath of boundedPaths) {
|
|
470
507
|
files[relPath] = parseFile(root, relPath, parseOpts);
|
|
471
508
|
}
|
|
472
509
|
|
|
@@ -511,6 +548,9 @@ export function buildRunExport(startDir = process.cwd(), exportOpts = {}) {
|
|
|
511
548
|
dashboard_session: buildDashboardSessionSummary(root),
|
|
512
549
|
delegation_summary: buildDelegationSummary(files),
|
|
513
550
|
repo_decisions: buildRepoDecisionsSummary(root, rawConfig),
|
|
551
|
+
export_files_truncated: exportFilesTruncated || false,
|
|
552
|
+
total_collected_files: collectedPaths.length,
|
|
553
|
+
included_files: Object.keys(files).length,
|
|
514
554
|
},
|
|
515
555
|
workspace: buildRunWorkspaceMetadata(root),
|
|
516
556
|
files,
|