agentxchain 2.29.0 → 2.31.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
|
@@ -10,6 +10,7 @@ const SECTION_DEFINITIONS = [
|
|
|
10
10
|
{ id: 'last_turn_verification', header: null, required: false },
|
|
11
11
|
{ id: 'blockers', header: 'Blockers', required: true },
|
|
12
12
|
{ id: 'escalation', header: 'Escalation', required: true },
|
|
13
|
+
{ id: 'workflow_artifacts', header: 'Workflow Artifacts', required: false },
|
|
13
14
|
{ id: 'gate_required_files', header: 'Gate Required Files', required: false },
|
|
14
15
|
{ id: 'phase_gate_status', header: 'Phase Gate Status', required: false },
|
|
15
16
|
];
|
|
@@ -88,6 +89,7 @@ export function renderContextSections(sections) {
|
|
|
88
89
|
|
|
89
90
|
appendTopLevelSection(lines, 'Blockers', [sectionMap.get('blockers')?.content]);
|
|
90
91
|
appendTopLevelSection(lines, 'Escalation', [sectionMap.get('escalation')?.content]);
|
|
92
|
+
appendTopLevelSection(lines, 'Workflow Artifacts', [sectionMap.get('workflow_artifacts')?.content]);
|
|
91
93
|
appendTopLevelSection(lines, 'Gate Required Files', [sectionMap.get('gate_required_files')?.content]);
|
|
92
94
|
appendTopLevelSection(lines, 'Phase Gate Status', [sectionMap.get('phase_gate_status')?.content]);
|
|
93
95
|
|
|
@@ -248,6 +248,31 @@ function renderPrompt(role, roleId, turn, state, config, root) {
|
|
|
248
248
|
lines.push('');
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
const workflowResponsibilities = getWorkflowPromptResponsibilities(config, phase, roleId, root);
|
|
252
|
+
if (workflowResponsibilities.length > 0) {
|
|
253
|
+
const isReviewOnlyOwner = role.write_authority === 'review_only';
|
|
254
|
+
lines.push('## Workflow-Kit Responsibilities');
|
|
255
|
+
lines.push('');
|
|
256
|
+
if (isReviewOnlyOwner) {
|
|
257
|
+
lines.push(`You are accountable for reviewing and attesting to these workflow-kit artifacts in phase \`${phase}\`:`);
|
|
258
|
+
} else {
|
|
259
|
+
lines.push(`You are accountable for producing these workflow-kit artifacts in phase \`${phase}\`:`);
|
|
260
|
+
}
|
|
261
|
+
lines.push('');
|
|
262
|
+
for (const artifact of workflowResponsibilities) {
|
|
263
|
+
const requiredLabel = artifact.required ? 'required' : 'optional';
|
|
264
|
+
const semanticsLabel = artifact.semantics ? `\`${artifact.semantics}\`` : '—';
|
|
265
|
+
lines.push(`- \`${artifact.path}\` — ${requiredLabel}; semantics: ${semanticsLabel}; status: ${artifact.status}`);
|
|
266
|
+
}
|
|
267
|
+
lines.push('');
|
|
268
|
+
if (isReviewOnlyOwner) {
|
|
269
|
+
lines.push('You cannot write repo files directly. Your accountability means you must confirm these artifacts exist, meet quality standards, and satisfy their semantic requirements. If a required artifact you own is missing, escalate to the producing role — do not request phase transition.');
|
|
270
|
+
} else {
|
|
271
|
+
lines.push('Do not request phase transition or run completion while a required workflow-kit artifact you own is missing or incomplete.');
|
|
272
|
+
}
|
|
273
|
+
lines.push('');
|
|
274
|
+
}
|
|
275
|
+
|
|
251
276
|
// Gate requirements
|
|
252
277
|
if (gateConfig) {
|
|
253
278
|
lines.push('## Phase Exit Gate');
|
|
@@ -421,6 +446,47 @@ function renderPrompt(role, roleId, turn, state, config, root) {
|
|
|
421
446
|
};
|
|
422
447
|
}
|
|
423
448
|
|
|
449
|
+
function getWorkflowPromptResponsibilities(config, phase, roleId, root) {
|
|
450
|
+
const artifacts = config?.workflow_kit?.phases?.[phase]?.artifacts;
|
|
451
|
+
if (!Array.isArray(artifacts) || artifacts.length === 0) {
|
|
452
|
+
return [];
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const entryRole = config?.routing?.[phase]?.entry_role || null;
|
|
456
|
+
const responsibilities = [];
|
|
457
|
+
|
|
458
|
+
for (const artifact of artifacts) {
|
|
459
|
+
if (!artifact?.path) {
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const owner = typeof artifact.owned_by === 'string' && artifact.owned_by
|
|
464
|
+
? artifact.owned_by
|
|
465
|
+
: null;
|
|
466
|
+
const responsibleRole = owner || entryRole;
|
|
467
|
+
if (responsibleRole !== roleId) {
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const absPath = join(root, artifact.path);
|
|
472
|
+
let exists = false;
|
|
473
|
+
try {
|
|
474
|
+
exists = existsSync(absPath);
|
|
475
|
+
} catch {
|
|
476
|
+
exists = false;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
responsibilities.push({
|
|
480
|
+
path: artifact.path,
|
|
481
|
+
required: artifact.required !== false,
|
|
482
|
+
semantics: artifact.semantics || null,
|
|
483
|
+
status: exists ? 'exists' : 'MISSING',
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return responsibilities;
|
|
488
|
+
}
|
|
489
|
+
|
|
424
490
|
// ── Context Rendering ───────────────────────────────────────────────────────
|
|
425
491
|
|
|
426
492
|
function renderContext(state, config, root, turn, role) {
|
|
@@ -621,8 +687,59 @@ function renderContext(state, config, root, turn, role) {
|
|
|
621
687
|
lines.push('');
|
|
622
688
|
}
|
|
623
689
|
|
|
624
|
-
//
|
|
690
|
+
// Workflow-kit artifacts for the current phase
|
|
625
691
|
const phase = state.phase;
|
|
692
|
+
const wkArtifacts = config?.workflow_kit?.phases?.[phase]?.artifacts;
|
|
693
|
+
if (Array.isArray(wkArtifacts) && wkArtifacts.length > 0) {
|
|
694
|
+
lines.push('## Workflow Artifacts');
|
|
695
|
+
lines.push('');
|
|
696
|
+
lines.push(`Current phase **${phase}** declares the following artifacts:`);
|
|
697
|
+
lines.push('');
|
|
698
|
+
lines.push('| Artifact | Required | Semantics | Owner | Status |');
|
|
699
|
+
lines.push('|----------|----------|-----------|-------|--------|');
|
|
700
|
+
const isReviewRole = role?.write_authority === 'review_only';
|
|
701
|
+
const reviewPreviews = [];
|
|
702
|
+
for (const art of wkArtifacts) {
|
|
703
|
+
if (!art?.path) continue;
|
|
704
|
+
const absPath = join(root, art.path);
|
|
705
|
+
let exists = false;
|
|
706
|
+
try { exists = existsSync(absPath); } catch { /* treat as missing */ }
|
|
707
|
+
const req = art.required !== false ? 'yes' : 'no';
|
|
708
|
+
const owner = art.owned_by || '—';
|
|
709
|
+
const status = exists ? 'exists' : 'MISSING';
|
|
710
|
+
const semCol = art.semantics ? `\`${art.semantics}\`` : '—';
|
|
711
|
+
lines.push(`| \`${art.path}\` | ${req} | ${semCol} | ${owner} | ${status} |`);
|
|
712
|
+
if (isReviewRole && exists) {
|
|
713
|
+
reviewPreviews.push(art);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
lines.push('');
|
|
717
|
+
if (reviewPreviews.length > 0) {
|
|
718
|
+
for (const art of reviewPreviews) {
|
|
719
|
+
const absPath = join(root, art.path);
|
|
720
|
+
const preview = buildGateFilePreview(absPath);
|
|
721
|
+
if (preview) {
|
|
722
|
+
lines.push(`### \`${art.path}\``);
|
|
723
|
+
lines.push('');
|
|
724
|
+
const semantic = extractGateFileSemantic(art.path, preview.raw);
|
|
725
|
+
if (semantic) {
|
|
726
|
+
lines.push(`**Semantic: ${semantic}**`);
|
|
727
|
+
lines.push('');
|
|
728
|
+
}
|
|
729
|
+
lines.push('```');
|
|
730
|
+
lines.push(preview.content);
|
|
731
|
+
lines.push('```');
|
|
732
|
+
if (preview.truncated) {
|
|
733
|
+
lines.push('');
|
|
734
|
+
lines.push(`_Preview truncated after ${GATE_FILE_PREVIEW_MAX_LINES} lines._`);
|
|
735
|
+
}
|
|
736
|
+
lines.push('');
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Phase gate requirements
|
|
626
743
|
const routing = config.routing?.[phase];
|
|
627
744
|
const exitGate = routing?.exit_gate;
|
|
628
745
|
const gateConfig = exitGate ? config.gates?.[exitGate] : null;
|
package/src/lib/export.js
CHANGED
|
@@ -628,6 +628,25 @@ export function validateWorkflowKitConfig(wk, routing, roles) {
|
|
|
628
628
|
errors.push(`${prefix} owned_by "${artifact.owned_by}" is not a valid role ID (must be lowercase alphanumeric with hyphens/underscores)`);
|
|
629
629
|
} else if (roles && typeof roles === 'object' && !roles[artifact.owned_by]) {
|
|
630
630
|
errors.push(`${prefix} owned_by "${artifact.owned_by}" does not reference a defined role`);
|
|
631
|
+
} else if (
|
|
632
|
+
artifact.required !== false &&
|
|
633
|
+
roles && typeof roles === 'object' &&
|
|
634
|
+
roles[artifact.owned_by]?.write_authority === 'review_only'
|
|
635
|
+
) {
|
|
636
|
+
// Check if any authoritative/proposed role exists in this phase's routing
|
|
637
|
+
const phaseRouting = routing?.[phase];
|
|
638
|
+
const phaseRoles = new Set([
|
|
639
|
+
...(phaseRouting?.allowed_next_roles || []),
|
|
640
|
+
...(phaseRouting?.entry_role ? [phaseRouting.entry_role] : []),
|
|
641
|
+
]);
|
|
642
|
+
const hasWriter = [...phaseRoles].some(rid =>
|
|
643
|
+
roles[rid]?.write_authority === 'authoritative' || roles[rid]?.write_authority === 'proposed',
|
|
644
|
+
);
|
|
645
|
+
if (!hasWriter) {
|
|
646
|
+
warnings.push(
|
|
647
|
+
`${prefix} owned_by "${artifact.owned_by}" is a review_only role in phase "${phase}" with no authoritative or proposed role — nobody can write this required artifact`,
|
|
648
|
+
);
|
|
649
|
+
}
|
|
631
650
|
}
|
|
632
651
|
}
|
|
633
652
|
}
|
package/src/lib/report.js
CHANGED
|
@@ -471,6 +471,38 @@ function deriveRepoStatusCounts(repoStatuses) {
|
|
|
471
471
|
return counts;
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
+
export function extractWorkflowKitArtifacts(artifact) {
|
|
475
|
+
const config = artifact.config;
|
|
476
|
+
if (!config || typeof config !== 'object' || !config.workflow_kit) return null;
|
|
477
|
+
|
|
478
|
+
const phase = artifact.summary?.phase || artifact.state?.phase;
|
|
479
|
+
if (!phase) return null;
|
|
480
|
+
|
|
481
|
+
const phaseConfig = config.workflow_kit.phases?.[phase];
|
|
482
|
+
if (!phaseConfig) return [];
|
|
483
|
+
|
|
484
|
+
const artifacts = Array.isArray(phaseConfig.artifacts) ? phaseConfig.artifacts : [];
|
|
485
|
+
if (artifacts.length === 0) return [];
|
|
486
|
+
|
|
487
|
+
const entryRole = config.routing?.[phase]?.entry_role || null;
|
|
488
|
+
const fileKeys = new Set(Object.keys(artifact.files || {}));
|
|
489
|
+
|
|
490
|
+
return artifacts
|
|
491
|
+
.filter((a) => a && typeof a.path === 'string')
|
|
492
|
+
.map((a) => {
|
|
493
|
+
const hasExplicitOwner = typeof a.owned_by === 'string' && a.owned_by.length > 0;
|
|
494
|
+
return {
|
|
495
|
+
path: a.path,
|
|
496
|
+
required: a.required !== false,
|
|
497
|
+
semantics: a.semantics || null,
|
|
498
|
+
owned_by: hasExplicitOwner ? a.owned_by : entryRole,
|
|
499
|
+
owner_resolution: hasExplicitOwner ? 'explicit' : 'entry_role',
|
|
500
|
+
exists: fileKeys.has(a.path),
|
|
501
|
+
};
|
|
502
|
+
})
|
|
503
|
+
.sort((a, b) => a.path.localeCompare(b.path, 'en'));
|
|
504
|
+
}
|
|
505
|
+
|
|
474
506
|
function buildRunSubject(artifact) {
|
|
475
507
|
const activeTurns = artifact.summary?.active_turn_ids || [];
|
|
476
508
|
const retainedTurns = artifact.summary?.retained_turn_ids || [];
|
|
@@ -518,6 +550,7 @@ function buildRunSubject(artifact) {
|
|
|
518
550
|
gate_summary: gateSummary,
|
|
519
551
|
intake_links: intakeLinks,
|
|
520
552
|
recovery_summary: recoverySummary,
|
|
553
|
+
workflow_kit_artifacts: extractWorkflowKitArtifacts(artifact),
|
|
521
554
|
},
|
|
522
555
|
artifacts: {
|
|
523
556
|
history_entries: artifact.summary?.history_entries || 0,
|
|
@@ -810,6 +843,17 @@ export function formatGovernanceReportText(report) {
|
|
|
810
843
|
lines.push(` Turn retained: ${run.recovery_summary.turn_retained == null ? 'n/a' : yesNo(run.recovery_summary.turn_retained)}`);
|
|
811
844
|
}
|
|
812
845
|
|
|
846
|
+
if (Array.isArray(run.workflow_kit_artifacts) && run.workflow_kit_artifacts.length > 0) {
|
|
847
|
+
lines.push('', `Workflow Artifacts (${run.phase || 'unknown'} phase):`);
|
|
848
|
+
for (const art of run.workflow_kit_artifacts) {
|
|
849
|
+
const req = art.required ? 'required' : 'optional';
|
|
850
|
+
const sem = art.semantics || 'none';
|
|
851
|
+
const owner = art.owned_by ? `${art.owned_by} (${art.owner_resolution})` : 'none';
|
|
852
|
+
const status = art.exists ? 'exists' : 'missing';
|
|
853
|
+
lines.push(` ${art.path} | ${req} | ${sem} | owner: ${owner} | ${status}`);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
813
857
|
return lines.join('\n');
|
|
814
858
|
}
|
|
815
859
|
|
|
@@ -1066,6 +1110,19 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1066
1110
|
lines.push(`- Turn retained: \`${run.recovery_summary.turn_retained == null ? 'n/a' : yesNo(run.recovery_summary.turn_retained)}\``);
|
|
1067
1111
|
}
|
|
1068
1112
|
|
|
1113
|
+
if (Array.isArray(run.workflow_kit_artifacts) && run.workflow_kit_artifacts.length > 0) {
|
|
1114
|
+
lines.push('', '## Workflow Artifacts', '');
|
|
1115
|
+
lines.push(`Phase: \`${run.phase || 'unknown'}\``, '');
|
|
1116
|
+
lines.push('| Artifact | Required | Semantics | Owner | Resolution | Status |', '|----------|----------|-----------|-------|------------|--------|');
|
|
1117
|
+
for (const art of run.workflow_kit_artifacts) {
|
|
1118
|
+
const req = art.required ? 'yes' : 'no';
|
|
1119
|
+
const sem = art.semantics ? `\`${art.semantics}\`` : 'none';
|
|
1120
|
+
const owner = art.owned_by ? `\`${art.owned_by}\`` : 'none';
|
|
1121
|
+
const status = art.exists ? 'exists' : '**missing**';
|
|
1122
|
+
lines.push(`| \`${art.path}\` | ${req} | ${sem} | ${owner} | ${art.owner_resolution} | ${status} |`);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1069
1126
|
return lines.join('\n');
|
|
1070
1127
|
}
|
|
1071
1128
|
|