agentxchain 2.66.1 → 2.67.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 +1 -1
- package/src/commands/status.js +20 -1
- package/src/commands/turn.js +29 -0
- package/src/lib/governed-state.js +8 -0
- package/src/lib/report.js +24 -3
package/package.json
CHANGED
package/src/commands/status.js
CHANGED
|
@@ -141,7 +141,16 @@ function renderGovernedStatus(context, opts) {
|
|
|
141
141
|
const statusLabel = turn.status === 'conflicted'
|
|
142
142
|
? chalk.red('conflicted')
|
|
143
143
|
: turn.status;
|
|
144
|
-
|
|
144
|
+
let elapsedTag = '';
|
|
145
|
+
if (turn.started_at) {
|
|
146
|
+
const elMs = Date.now() - new Date(turn.started_at).getTime();
|
|
147
|
+
if (elMs >= 0) {
|
|
148
|
+
const s = Math.floor(elMs / 1000);
|
|
149
|
+
const m = Math.floor(s / 60);
|
|
150
|
+
elapsedTag = m > 0 ? ` — ${m}m ${s % 60}s` : ` — ${s}s`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
console.log(` ${marker} ${turn.turn_id} — ${chalk.bold(turn.assigned_role)} (${statusLabel}) [attempt ${turn.attempt}]${elapsedTag}`);
|
|
145
154
|
if (turn.status === 'conflicted' && turn.conflict_state) {
|
|
146
155
|
const cs = turn.conflict_state;
|
|
147
156
|
const files = cs.conflict_error?.conflicting_files || [];
|
|
@@ -161,6 +170,16 @@ function renderGovernedStatus(context, opts) {
|
|
|
161
170
|
console.log(` ${chalk.dim('Role:')} ${chalk.bold(singleActiveTurn.assigned_role)} (${singleActiveTurn.status})`);
|
|
162
171
|
console.log(` ${chalk.dim('Runtime:')} ${singleActiveTurn.runtime_id}`);
|
|
163
172
|
console.log(` ${chalk.dim('Attempt:')} ${singleActiveTurn.attempt}`);
|
|
173
|
+
if (singleActiveTurn.started_at) {
|
|
174
|
+
const elapsedMs = Date.now() - new Date(singleActiveTurn.started_at).getTime();
|
|
175
|
+
if (elapsedMs >= 0) {
|
|
176
|
+
const secs = Math.floor(elapsedMs / 1000);
|
|
177
|
+
const mins = Math.floor(secs / 60);
|
|
178
|
+
const remainSecs = secs % 60;
|
|
179
|
+
const elapsed = mins > 0 ? `${mins}m ${remainSecs}s` : `${remainSecs}s`;
|
|
180
|
+
console.log(` ${chalk.dim('Elapsed:')} ${elapsed}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
164
183
|
if (singleActiveTurn.status === 'conflicted' && singleActiveTurn.conflict_state) {
|
|
165
184
|
const cs = singleActiveTurn.conflict_state;
|
|
166
185
|
const files = cs.conflict_error?.conflicting_files || [];
|
package/src/commands/turn.js
CHANGED
|
@@ -119,6 +119,7 @@ function buildArtifactIndex(root, turnId) {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
function buildTurnPayload(turnId, turn, state, artifacts, assignment) {
|
|
122
|
+
const elapsedMs = getElapsedMs(turn.started_at);
|
|
122
123
|
return {
|
|
123
124
|
turn_id: turnId,
|
|
124
125
|
run_id: state.run_id || assignment?.run_id || null,
|
|
@@ -127,6 +128,8 @@ function buildTurnPayload(turnId, turn, state, artifacts, assignment) {
|
|
|
127
128
|
runtime: turn.runtime_id,
|
|
128
129
|
status: turn.status,
|
|
129
130
|
attempt: turn.attempt,
|
|
131
|
+
started_at: turn.started_at || null,
|
|
132
|
+
elapsed_ms: elapsedMs,
|
|
130
133
|
dispatch_dir: getDispatchTurnDir(turnId),
|
|
131
134
|
staging_result_path: assignment?.staging_result_path || null,
|
|
132
135
|
active_turn_count: getActiveTurnCount(state),
|
|
@@ -140,6 +143,7 @@ function buildTurnPayload(turnId, turn, state, artifacts, assignment) {
|
|
|
140
143
|
}
|
|
141
144
|
|
|
142
145
|
function printTurnSummary(turnId, turn, state, artifacts, assignment) {
|
|
146
|
+
const elapsedMs = getElapsedMs(turn.started_at);
|
|
143
147
|
console.log('');
|
|
144
148
|
console.log(chalk.bold(` Turn: ${chalk.cyan(turnId)}`));
|
|
145
149
|
console.log(chalk.dim(' ' + '─'.repeat(44)));
|
|
@@ -149,6 +153,12 @@ function printTurnSummary(turnId, turn, state, artifacts, assignment) {
|
|
|
149
153
|
console.log(` ${chalk.dim('Runtime:')} ${turn.runtime_id}`);
|
|
150
154
|
console.log(` ${chalk.dim('Status:')} ${turn.status}`);
|
|
151
155
|
console.log(` ${chalk.dim('Attempt:')} ${turn.attempt}`);
|
|
156
|
+
if (turn.started_at) {
|
|
157
|
+
console.log(` ${chalk.dim('Started:')} ${turn.started_at}`);
|
|
158
|
+
}
|
|
159
|
+
if (elapsedMs != null) {
|
|
160
|
+
console.log(` ${chalk.dim('Elapsed:')} ${formatElapsed(elapsedMs)}`);
|
|
161
|
+
}
|
|
152
162
|
console.log(` ${chalk.dim('Dispatch:')} ${getDispatchTurnDir(turnId)}`);
|
|
153
163
|
if (assignment?.staging_result_path) {
|
|
154
164
|
console.log(` ${chalk.dim('Staging:')} ${assignment.staging_result_path}`);
|
|
@@ -208,3 +218,22 @@ function readJsonArtifact(absPath) {
|
|
|
208
218
|
return null;
|
|
209
219
|
}
|
|
210
220
|
}
|
|
221
|
+
|
|
222
|
+
function getElapsedMs(startedAt) {
|
|
223
|
+
if (typeof startedAt !== 'string') {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
const started = Date.parse(startedAt);
|
|
227
|
+
if (!Number.isFinite(started)) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
const elapsed = Date.now() - started;
|
|
231
|
+
return elapsed >= 0 ? elapsed : null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function formatElapsed(ms) {
|
|
235
|
+
const totalSeconds = Math.floor(ms / 1000);
|
|
236
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
237
|
+
const seconds = totalSeconds % 60;
|
|
238
|
+
return minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
|
239
|
+
}
|
|
@@ -2590,7 +2590,9 @@ function _acceptGovernedTurnLocked(root, config, opts) {
|
|
|
2590
2590
|
accepted_sequence: acceptedSequence,
|
|
2591
2591
|
concurrent_with: Array.isArray(currentTurn.concurrent_with) ? currentTurn.concurrent_with : [],
|
|
2592
2592
|
cost: turnResult.cost || {},
|
|
2593
|
+
...(currentTurn.started_at ? { started_at: currentTurn.started_at } : {}),
|
|
2593
2594
|
accepted_at: now,
|
|
2595
|
+
...(currentTurn.started_at ? { duration_ms: Math.max(0, new Date(now).getTime() - new Date(currentTurn.started_at).getTime()) } : {}),
|
|
2594
2596
|
};
|
|
2595
2597
|
const nextHistoryEntries = [...historyEntries, historyEntry];
|
|
2596
2598
|
// Build ledger entries for the journal
|
|
@@ -3081,11 +3083,17 @@ function _acceptGovernedTurnLocked(root, config, opts) {
|
|
|
3081
3083
|
}
|
|
3082
3084
|
|
|
3083
3085
|
// Emit turn_accepted event to local log.
|
|
3086
|
+
const turnAcceptedPayload = {};
|
|
3087
|
+
if (currentTurn.started_at) {
|
|
3088
|
+
turnAcceptedPayload.started_at = currentTurn.started_at;
|
|
3089
|
+
turnAcceptedPayload.duration_ms = Math.max(0, new Date(now).getTime() - new Date(currentTurn.started_at).getTime());
|
|
3090
|
+
}
|
|
3084
3091
|
emitRunEvent(root, 'turn_accepted', {
|
|
3085
3092
|
run_id: updatedState.run_id,
|
|
3086
3093
|
phase: updatedState.phase,
|
|
3087
3094
|
status: updatedState.status,
|
|
3088
3095
|
turn: { turn_id: currentTurn.turn_id, role_id: currentTurn.assigned_role },
|
|
3096
|
+
payload: turnAcceptedPayload,
|
|
3089
3097
|
});
|
|
3090
3098
|
|
|
3091
3099
|
if (updatedState.status === 'blocked') {
|
package/src/lib/report.js
CHANGED
|
@@ -66,6 +66,25 @@ function formatUsd(value) {
|
|
|
66
66
|
return typeof value === 'number' ? `$${value.toFixed(2)}` : 'n/a';
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
function formatDurationCompact(ms) {
|
|
70
|
+
if (typeof ms !== 'number' || !Number.isFinite(ms) || ms < 0) return null;
|
|
71
|
+
if (ms < 1000) return `${ms}ms`;
|
|
72
|
+
const secs = Math.floor(ms / 1000);
|
|
73
|
+
if (secs < 60) return `${secs}s`;
|
|
74
|
+
const mins = Math.floor(secs / 60);
|
|
75
|
+
const remainSecs = secs % 60;
|
|
76
|
+
if (mins < 60) return `${mins}m ${remainSecs}s`;
|
|
77
|
+
const hrs = Math.floor(mins / 60);
|
|
78
|
+
const remainMins = mins % 60;
|
|
79
|
+
return `${hrs}h ${remainMins}m`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function formatTurnTimelineTime(turn) {
|
|
83
|
+
const acceptedAt = turn.accepted_at || 'n/a';
|
|
84
|
+
const duration = formatDurationCompact(turn.duration_ms);
|
|
85
|
+
return duration ? `${acceptedAt} (${duration})` : acceptedAt;
|
|
86
|
+
}
|
|
87
|
+
|
|
69
88
|
function formatStatusCounts(statusCounts) {
|
|
70
89
|
const entries = Object.entries(statusCounts || {}).sort(([left], [right]) => left.localeCompare(right, 'en'));
|
|
71
90
|
if (entries.length === 0) return 'none';
|
|
@@ -99,6 +118,8 @@ function extractHistoryTimeline(artifact) {
|
|
|
99
118
|
decisions: Array.isArray(e.decisions) ? e.decisions.map((d) => d?.id || d).filter(Boolean) : [],
|
|
100
119
|
objections: Array.isArray(e.objections) ? e.objections.map((o) => o?.id || o).filter(Boolean) : [],
|
|
101
120
|
cost_usd: typeof e.cost?.total_usd === 'number' ? e.cost.total_usd : null,
|
|
121
|
+
started_at: e.started_at || null,
|
|
122
|
+
duration_ms: typeof e.duration_ms === 'number' ? e.duration_ms : null,
|
|
102
123
|
accepted_at: e.accepted_at || null,
|
|
103
124
|
}));
|
|
104
125
|
}
|
|
@@ -943,7 +964,7 @@ export function formatGovernanceReportText(report) {
|
|
|
943
964
|
const cost = t.cost_usd != null ? formatUsd(t.cost_usd) : 'n/a';
|
|
944
965
|
const phase = t.phase_transition ? `${t.phase || '?'} -> ${t.phase_transition}` : (t.phase || '?');
|
|
945
966
|
const siblingNote = Array.isArray(t.sibling_attributed_files) ? ` (${t.sibling_attributed_files.length} sibling-attributed)` : '';
|
|
946
|
-
lines.push(` ${i + 1}. [${t.role}] ${t.summary || '(no summary)'} | phase: ${phase} | files: ${t.files_changed_count}${siblingNote} | cost: ${cost} | ${t
|
|
967
|
+
lines.push(` ${i + 1}. [${t.role}] ${t.summary || '(no summary)'} | phase: ${phase} | files: ${t.files_changed_count}${siblingNote} | cost: ${cost} | ${formatTurnTimelineTime(t)}`);
|
|
947
968
|
}
|
|
948
969
|
}
|
|
949
970
|
|
|
@@ -1353,7 +1374,7 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1353
1374
|
const phase = t.phase_transition ? `${t.phase || '?'} → ${t.phase_transition}` : (t.phase || '?');
|
|
1354
1375
|
const summary = (t.summary || '(no summary)').replace(/\|/g, '\\|');
|
|
1355
1376
|
const siblingNote = Array.isArray(t.sibling_attributed_files) ? ` (${t.sibling_attributed_files.length} sibling)` : '';
|
|
1356
|
-
lines.push(`| ${i + 1} | ${t.role} | ${phase} | ${summary} | ${t.files_changed_count}${siblingNote} | ${cost} | ${t.
|
|
1377
|
+
lines.push(`| ${i + 1} | ${t.role} | ${phase} | ${summary} | ${t.files_changed_count}${siblingNote} | ${cost} | ${formatTurnTimelineTime(t).replace(/\|/g, '\\|')} |`);
|
|
1357
1378
|
}
|
|
1358
1379
|
}
|
|
1359
1380
|
|
|
@@ -1611,7 +1632,7 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1611
1632
|
const phase = t.phase_transition ? `${t.phase || '?'} → ${t.phase_transition}` : (t.phase || '?');
|
|
1612
1633
|
const summary = (t.summary || '(no summary)').replace(/\|/g, '\\|');
|
|
1613
1634
|
const siblingNote = Array.isArray(t.sibling_attributed_files) ? ` (${t.sibling_attributed_files.length} sibling)` : '';
|
|
1614
|
-
repoLines.push(`| ${i + 1} | ${t.role} | ${phase} | ${summary} | ${t.files_changed_count}${siblingNote} | ${cost} | ${t.
|
|
1635
|
+
repoLines.push(`| ${i + 1} | ${t.role} | ${phase} | ${summary} | ${t.files_changed_count}${siblingNote} | ${cost} | ${formatTurnTimelineTime(t).replace(/\|/g, '\\|')} |`);
|
|
1615
1636
|
}
|
|
1616
1637
|
}
|
|
1617
1638
|
if (repo.decisions && repo.decisions.length > 0) {
|