agentxchain 2.55.0 → 2.57.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/bin/agentxchain.js +1 -0
- package/package.json +1 -1
- package/src/commands/demo.js +1 -1
- package/src/commands/init.js +7 -0
- package/src/commands/status.js +4 -0
- package/src/lib/dispatch-bundle.js +9 -0
- package/src/lib/export.js +2 -0
- package/src/lib/normalized-config.js +11 -0
- package/src/lib/report.js +3 -0
- package/src/lib/run-context-inheritance.js +16 -0
- package/src/lib/run-history.js +62 -1
package/bin/agentxchain.js
CHANGED
|
@@ -128,6 +128,7 @@ program
|
|
|
128
128
|
.option('--template <id>', 'Governed scaffold template: generic, api-service, cli-tool, library, web-app, enterprise-app')
|
|
129
129
|
.option('--dev-command <parts...>', 'Governed local-dev command parts. Include {prompt} for argv prompt delivery.')
|
|
130
130
|
.option('--dev-prompt-transport <mode>', 'Governed local-dev prompt transport: argv, stdin, dispatch_bundle_only')
|
|
131
|
+
.option('--goal <text>', 'Project goal — persisted in config and rendered in every dispatch bundle')
|
|
131
132
|
.option('--schema-version <version>', 'Schema version (3 for legacy, or use --governed for current)')
|
|
132
133
|
.action(initCommand);
|
|
133
134
|
|
package/package.json
CHANGED
package/src/commands/demo.js
CHANGED
|
@@ -19,7 +19,7 @@ function makeConfig() {
|
|
|
19
19
|
return {
|
|
20
20
|
schema_version: 4,
|
|
21
21
|
protocol_mode: 'governed',
|
|
22
|
-
project: { id: 'agentxchain-demo', name: 'AgentXchain Demo', default_branch: 'main' },
|
|
22
|
+
project: { id: 'agentxchain-demo', name: 'AgentXchain Demo', goal: 'Build an auth token rotation service with expiry, graceful rollover, and audit logging', default_branch: 'main' },
|
|
23
23
|
roles: {
|
|
24
24
|
pm: {
|
|
25
25
|
title: 'Product Manager',
|
package/src/commands/init.js
CHANGED
|
@@ -656,12 +656,14 @@ export function scaffoldGoverned(dir, projectName, projectId, templateId = 'gene
|
|
|
656
656
|
.filter(Boolean)
|
|
657
657
|
)].map((gateId) => [gateId, 'pending'])
|
|
658
658
|
);
|
|
659
|
+
const projectGoal = runtimeOptions.goal;
|
|
659
660
|
const config = {
|
|
660
661
|
schema_version: '1.0',
|
|
661
662
|
template: template.id,
|
|
662
663
|
project: {
|
|
663
664
|
id: projectId,
|
|
664
665
|
name: projectName,
|
|
666
|
+
...(typeof projectGoal === 'string' && projectGoal.trim() ? { goal: projectGoal.trim() } : {}),
|
|
665
667
|
default_branch: 'main'
|
|
666
668
|
},
|
|
667
669
|
roles,
|
|
@@ -974,6 +976,11 @@ async function initGoverned(opts) {
|
|
|
974
976
|
console.log(` ${chalk.bold('agentxchain step')} ${chalk.dim('# run the first governed turn')}`);
|
|
975
977
|
console.log(` ${chalk.bold('agentxchain status')} ${chalk.dim('# inspect phase, gate, and turn state')}`);
|
|
976
978
|
console.log('');
|
|
979
|
+
if (!config?.project?.goal) {
|
|
980
|
+
console.log(` ${chalk.dim('Tip:')} Add a project goal to guide agent context:`);
|
|
981
|
+
console.log(` ${chalk.bold('agentxchain init --governed --goal "Build a ..."')} ${chalk.dim('# or set project.goal in agentxchain.json')}`);
|
|
982
|
+
console.log('');
|
|
983
|
+
}
|
|
977
984
|
console.log(` ${chalk.dim('Guide:')} https://agentxchain.dev/docs/getting-started`);
|
|
978
985
|
console.log('');
|
|
979
986
|
}
|
package/src/commands/status.js
CHANGED
|
@@ -89,6 +89,7 @@ function renderGovernedStatus(context, opts) {
|
|
|
89
89
|
version,
|
|
90
90
|
protocol_mode: config.protocol_mode,
|
|
91
91
|
template: config.template || 'generic',
|
|
92
|
+
project_goal: config.project?.goal || null,
|
|
92
93
|
config,
|
|
93
94
|
state,
|
|
94
95
|
provenance: state?.provenance || null,
|
|
@@ -106,6 +107,9 @@ function renderGovernedStatus(context, opts) {
|
|
|
106
107
|
console.log('');
|
|
107
108
|
|
|
108
109
|
console.log(` ${chalk.dim('Project:')} ${config.project.name}`);
|
|
110
|
+
if (config.project.goal) {
|
|
111
|
+
console.log(` ${chalk.dim('Goal:')} ${config.project.goal}`);
|
|
112
|
+
}
|
|
109
113
|
console.log(` ${chalk.dim('Protocol:')} ${chalk.cyan(`governed (v${version})`)}`);
|
|
110
114
|
console.log(` ${chalk.dim('Template:')} ${config.template || 'generic'}`);
|
|
111
115
|
console.log(` ${chalk.dim('Phase:')} ${state?.phase ? formatGovernedPhase(state.phase) : chalk.dim('unknown')}`);
|
|
@@ -512,6 +512,15 @@ function renderContext(state, config, root, turn, role) {
|
|
|
512
512
|
}
|
|
513
513
|
lines.push('');
|
|
514
514
|
|
|
515
|
+
// Project goal (when set in agentxchain.json)
|
|
516
|
+
const projectGoal = config?.project?.goal;
|
|
517
|
+
if (typeof projectGoal === 'string' && projectGoal.trim()) {
|
|
518
|
+
lines.push('## Project Goal');
|
|
519
|
+
lines.push('');
|
|
520
|
+
lines.push(projectGoal.trim());
|
|
521
|
+
lines.push('');
|
|
522
|
+
}
|
|
523
|
+
|
|
515
524
|
// Inherited context from parent run (when --inherit-context was used)
|
|
516
525
|
if (state.inherited_context) {
|
|
517
526
|
// First turn gets the full rendering; subsequent turns get compact
|
package/src/lib/export.js
CHANGED
|
@@ -295,11 +295,13 @@ export function buildRunExport(startDir = process.cwd()) {
|
|
|
295
295
|
project: {
|
|
296
296
|
id: config.project.id,
|
|
297
297
|
name: config.project.name,
|
|
298
|
+
goal: config.project.goal || null,
|
|
298
299
|
template: config.template || 'generic',
|
|
299
300
|
protocol_mode: config.protocol_mode,
|
|
300
301
|
schema_version: version,
|
|
301
302
|
},
|
|
302
303
|
summary: {
|
|
304
|
+
project_goal: config.project.goal || null,
|
|
303
305
|
run_id: state?.run_id || null,
|
|
304
306
|
status: state?.status || null,
|
|
305
307
|
phase: state?.phase || null,
|
|
@@ -340,6 +340,16 @@ export function validateV4Config(data, projectRoot) {
|
|
|
340
340
|
} else {
|
|
341
341
|
if (typeof data.project.id !== 'string' || !data.project.id.trim()) errors.push('project.id must be a non-empty string');
|
|
342
342
|
if (typeof data.project.name !== 'string' || !data.project.name.trim()) errors.push('project.name must be a non-empty string');
|
|
343
|
+
// Optional project.goal field
|
|
344
|
+
if (data.project.goal !== undefined && data.project.goal !== null) {
|
|
345
|
+
if (typeof data.project.goal !== 'string') {
|
|
346
|
+
errors.push('project.goal must be a string');
|
|
347
|
+
} else if (!data.project.goal.trim()) {
|
|
348
|
+
errors.push('project.goal must be a non-empty string when provided');
|
|
349
|
+
} else if (data.project.goal.trim().length > 500) {
|
|
350
|
+
errors.push('project.goal must be 500 characters or fewer');
|
|
351
|
+
}
|
|
352
|
+
}
|
|
343
353
|
}
|
|
344
354
|
|
|
345
355
|
// Roles
|
|
@@ -968,6 +978,7 @@ export function normalizeV4(raw) {
|
|
|
968
978
|
project: {
|
|
969
979
|
id: raw.project?.id || 'unknown',
|
|
970
980
|
name: raw.project?.name || 'Unknown',
|
|
981
|
+
...(typeof raw.project?.goal === 'string' && raw.project.goal.trim() ? { goal: raw.project.goal.trim() } : {}),
|
|
971
982
|
default_branch: raw.project?.default_branch || 'main',
|
|
972
983
|
},
|
|
973
984
|
roles,
|
package/src/lib/report.js
CHANGED
|
@@ -630,6 +630,7 @@ function buildRunSubject(artifact) {
|
|
|
630
630
|
project: {
|
|
631
631
|
id: artifact.project?.id || null,
|
|
632
632
|
name: artifact.project?.name || null,
|
|
633
|
+
goal: artifact.project?.goal || null,
|
|
633
634
|
template: artifact.project?.template || 'generic',
|
|
634
635
|
protocol_mode: artifact.project?.protocol_mode || null,
|
|
635
636
|
schema_version: artifact.project?.schema_version || null,
|
|
@@ -876,6 +877,7 @@ export function formatGovernanceReportText(report) {
|
|
|
876
877
|
`Export kind: ${report.export_kind}`,
|
|
877
878
|
'Verification: PASS',
|
|
878
879
|
`Project: ${project.name || 'unknown'} (${project.id || 'unknown'})`,
|
|
880
|
+
...(project.goal ? [`Goal: ${project.goal}`] : []),
|
|
879
881
|
`Template: ${project.template}`,
|
|
880
882
|
`Protocol: ${project.protocol_mode || 'unknown'} (config schema ${project.schema_version || 'unknown'})`,
|
|
881
883
|
`Run: ${run.run_id || 'none'}`,
|
|
@@ -1285,6 +1287,7 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1285
1287
|
`- Export kind: \`${report.export_kind}\``,
|
|
1286
1288
|
'- Verification: `pass`',
|
|
1287
1289
|
`- Project: ${project.name || 'unknown'} (\`${project.id || 'unknown'}\`)`,
|
|
1290
|
+
...(project.goal ? [`- Goal: ${project.goal}`] : []),
|
|
1288
1291
|
`- Template: \`${project.template}\``,
|
|
1289
1292
|
`- Protocol: \`${project.protocol_mode || 'unknown'}\` (config schema \`${project.schema_version || 'unknown'}\`)`,
|
|
1290
1293
|
`- Run: \`${run.run_id || 'none'}\``,
|
|
@@ -57,6 +57,7 @@ export function buildInheritedContext(root, parentRunId) {
|
|
|
57
57
|
parent_phases_completed: parentEntry.phases_completed || [],
|
|
58
58
|
parent_roles_used: parentEntry.roles_used || [],
|
|
59
59
|
parent_blocked_reason: parentEntry.blocked_reason || null,
|
|
60
|
+
parent_retrospective: parentEntry.retrospective || null,
|
|
60
61
|
recent_decisions: recentDecisions,
|
|
61
62
|
recent_accepted_turns: acceptedTurns,
|
|
62
63
|
inherited_at: new Date().toISOString(),
|
|
@@ -93,6 +94,20 @@ export function renderInheritedContextMarkdown(inheritedContext, compact = false
|
|
|
93
94
|
}
|
|
94
95
|
lines.push('');
|
|
95
96
|
|
|
97
|
+
if (!compact && inheritedContext.parent_retrospective) {
|
|
98
|
+
lines.push('### Parent Retrospective');
|
|
99
|
+
lines.push('');
|
|
100
|
+
lines.push(`- **Headline:** ${inheritedContext.parent_retrospective.headline || 'n/a'}`);
|
|
101
|
+
lines.push(`- **Terminal reason:** ${inheritedContext.parent_retrospective.terminal_reason || 'unknown'}`);
|
|
102
|
+
if (inheritedContext.parent_retrospective.next_operator_action) {
|
|
103
|
+
lines.push(`- **Next operator action:** ${inheritedContext.parent_retrospective.next_operator_action}`);
|
|
104
|
+
}
|
|
105
|
+
if (inheritedContext.parent_retrospective.follow_on_hint) {
|
|
106
|
+
lines.push(`- **Follow-on hint:** ${inheritedContext.parent_retrospective.follow_on_hint}`);
|
|
107
|
+
}
|
|
108
|
+
lines.push('');
|
|
109
|
+
}
|
|
110
|
+
|
|
96
111
|
if (!compact && inheritedContext.recent_decisions?.length) {
|
|
97
112
|
lines.push('### Recent Decisions');
|
|
98
113
|
lines.push('');
|
|
@@ -153,6 +168,7 @@ function buildPartialContext(parentRunId, parentEntry, decisions, turns, warning
|
|
|
153
168
|
parent_phases_completed: parentEntry?.phases_completed || [],
|
|
154
169
|
parent_roles_used: parentEntry?.roles_used || [],
|
|
155
170
|
parent_blocked_reason: parentEntry?.blocked_reason || null,
|
|
171
|
+
parent_retrospective: parentEntry?.retrospective || null,
|
|
156
172
|
recent_decisions: decisions,
|
|
157
173
|
recent_accepted_turns: turns,
|
|
158
174
|
inherited_at: new Date().toISOString(),
|
package/src/lib/run-history.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { readFileSync, appendFileSync, existsSync, mkdirSync } from 'fs';
|
|
11
11
|
import { join, dirname } from 'path';
|
|
12
12
|
import { normalizeRunProvenance } from './run-provenance.js';
|
|
13
|
+
import { deriveRecoveryDescriptor } from './blocked-state.js';
|
|
13
14
|
|
|
14
15
|
const RUN_HISTORY_PATH = '.agentxchain/run-history.jsonl';
|
|
15
16
|
const HISTORY_PATH = '.agentxchain/history.jsonl';
|
|
@@ -83,6 +84,12 @@ export function recordRunHistory(root, state, config, status) {
|
|
|
83
84
|
connector_used: connectorUsed,
|
|
84
85
|
model_used: modelUsed,
|
|
85
86
|
provenance: normalizeRunProvenance(state?.provenance),
|
|
87
|
+
retrospective: buildRunRetrospective({
|
|
88
|
+
state,
|
|
89
|
+
config,
|
|
90
|
+
status,
|
|
91
|
+
historyEntries,
|
|
92
|
+
}),
|
|
86
93
|
inheritance_snapshot: {
|
|
87
94
|
recent_decisions: buildRecentDecisionSnapshot(ledgerEntries),
|
|
88
95
|
recent_accepted_turns: buildRecentAcceptedTurnSnapshot(historyEntries),
|
|
@@ -300,7 +307,7 @@ function buildRecentDecisionSnapshot(entries) {
|
|
|
300
307
|
|
|
301
308
|
function buildRecentAcceptedTurnSnapshot(entries) {
|
|
302
309
|
return entries
|
|
303
|
-
.filter((entry) => entry
|
|
310
|
+
.filter((entry) => entry?.status !== 'rejected' && typeof entry?.turn_id === 'string' && typeof entry?.role === 'string')
|
|
304
311
|
.slice(-MAX_INHERITANCE_TURNS)
|
|
305
312
|
.map((entry) => ({
|
|
306
313
|
turn_id: entry.turn_id || null,
|
|
@@ -309,3 +316,57 @@ function buildRecentAcceptedTurnSnapshot(entries) {
|
|
|
309
316
|
phase: entry.phase || null,
|
|
310
317
|
}));
|
|
311
318
|
}
|
|
319
|
+
|
|
320
|
+
function buildRunRetrospective({ state, config, status, historyEntries }) {
|
|
321
|
+
const acceptedTurns = historyEntries.filter((entry) => entry && typeof entry === 'object');
|
|
322
|
+
const lastAcceptedTurn = acceptedTurns[acceptedTurns.length - 1] || null;
|
|
323
|
+
const recovery = status === 'blocked'
|
|
324
|
+
? deriveRecoveryDescriptor(state, config)
|
|
325
|
+
: null;
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
headline: buildRetrospectiveHeadline({ status, lastAcceptedTurn, recovery, acceptedTurns }),
|
|
329
|
+
terminal_reason: status === 'completed'
|
|
330
|
+
? 'completed'
|
|
331
|
+
: recovery?.typed_reason || 'blocked',
|
|
332
|
+
next_operator_action: status === 'blocked'
|
|
333
|
+
? recovery?.recovery_action || null
|
|
334
|
+
: null,
|
|
335
|
+
follow_on_hint: status === 'completed'
|
|
336
|
+
? buildFollowOnHint(state, lastAcceptedTurn)
|
|
337
|
+
: null,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function buildRetrospectiveHeadline({ status, lastAcceptedTurn, recovery, acceptedTurns }) {
|
|
342
|
+
if (status === 'blocked') {
|
|
343
|
+
if (typeof recovery?.detail === 'string' && recovery.detail.trim()) {
|
|
344
|
+
return recovery.detail.trim();
|
|
345
|
+
}
|
|
346
|
+
if (typeof lastAcceptedTurn?.summary === 'string' && lastAcceptedTurn.summary.trim()) {
|
|
347
|
+
return `Run blocked after: ${lastAcceptedTurn.summary.trim()}`;
|
|
348
|
+
}
|
|
349
|
+
return 'Run blocked.';
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (typeof lastAcceptedTurn?.summary === 'string' && lastAcceptedTurn.summary.trim()) {
|
|
353
|
+
return lastAcceptedTurn.summary.trim();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return `Run completed after ${acceptedTurns.length} accepted turn(s).`;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function buildFollowOnHint(state, lastAcceptedTurn) {
|
|
360
|
+
const runId = state?.run_id;
|
|
361
|
+
if (typeof runId !== 'string' || !runId.trim()) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const base = `If more scope remains, start a child run with \`agentxchain run --continue-from ${runId} --inherit-context\`.`;
|
|
366
|
+
const suggestedRole = lastAcceptedTurn?.proposed_next_role;
|
|
367
|
+
if (typeof suggestedRole === 'string' && suggestedRole.trim() && suggestedRole !== 'human') {
|
|
368
|
+
return `${base} The last accepted turn suggested \`${suggestedRole}\` next.`;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return base;
|
|
372
|
+
}
|