agentxchain 2.109.0 → 2.110.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/dashboard/components/blocked.js +4 -2
- package/dashboard/components/gate.js +6 -3
- package/package.json +1 -1
- package/src/commands/approve-completion.js +9 -4
- package/src/commands/approve-transition.js +9 -4
- package/src/commands/status.js +2 -2
- package/src/lib/gate-actions.js +33 -2
- package/src/lib/governed-state.js +6 -2
- package/src/lib/report.js +6 -3
|
@@ -120,8 +120,10 @@ function renderGateActionFailure(gateActions, state) {
|
|
|
120
120
|
html += `<div class="annotation-list">`;
|
|
121
121
|
for (const action of actions) {
|
|
122
122
|
const label = action.action_label || action.command || `action ${action.action_index || '?'}`;
|
|
123
|
-
const outcome = action.status === 'failed'
|
|
124
|
-
|
|
123
|
+
const outcome = action.status === 'failed'
|
|
124
|
+
? (action.timed_out ? `⏱ timed out after ${action.timeout_ms}ms` : '❌ failed')
|
|
125
|
+
: '✅ succeeded';
|
|
126
|
+
const exitStr = action.timed_out ? '' : (action.exit_code != null ? ` (exit ${action.exit_code})` : '');
|
|
125
127
|
html += `<div class="annotation-card">
|
|
126
128
|
<span class="mono">${esc(String(action.action_index || '?'))}.</span>
|
|
127
129
|
<span>${esc(label)}</span>
|
|
@@ -240,9 +240,12 @@ function renderGateActionsSection(gateActions) {
|
|
|
240
240
|
html += `<ul>`;
|
|
241
241
|
for (const a of attempt.actions) {
|
|
242
242
|
const aLabel = a.action_label || a.command || `action ${a.action_index || '?'}`;
|
|
243
|
-
const outcome = a.status === 'failed'
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
const outcome = a.status === 'failed'
|
|
244
|
+
? (a.timed_out ? '⏱' : '❌')
|
|
245
|
+
: '✅';
|
|
246
|
+
const timeoutStr = a.timed_out ? ` timed out after ${a.timeout_ms}ms` : '';
|
|
247
|
+
const exitStr = a.timed_out ? '' : (a.exit_code != null ? ` (exit ${a.exit_code})` : '');
|
|
248
|
+
html += `<li>${outcome} ${esc(aLabel)}${esc(exitStr)}${esc(timeoutStr)}</li>`;
|
|
246
249
|
}
|
|
247
250
|
html += `</ul>`;
|
|
248
251
|
}
|
package/package.json
CHANGED
|
@@ -44,7 +44,8 @@ export async function approveCompletionCommand(opts) {
|
|
|
44
44
|
if (result.gate_actions?.length > 0) {
|
|
45
45
|
console.log(` ${chalk.dim('Gate actions:')} ${result.gate_actions.length}`);
|
|
46
46
|
for (const action of result.gate_actions) {
|
|
47
|
-
|
|
47
|
+
const timeoutHint = action.timeout_ms && action.timeout_ms !== 900_000 ? chalk.dim(` [timeout: ${action.timeout_ms}ms]`) : '';
|
|
48
|
+
console.log(` ${action.index}. ${action.label || action.run}${timeoutHint}`);
|
|
48
49
|
if (action.label) {
|
|
49
50
|
console.log(` ${chalk.dim(action.run)}`);
|
|
50
51
|
}
|
|
@@ -103,6 +104,10 @@ function printGateHookFailure(result, gateType, gateInfo) {
|
|
|
103
104
|
|
|
104
105
|
function printGateActionFailure(result, gateInfo) {
|
|
105
106
|
const failure = result.gateActionRun?.failed_action;
|
|
107
|
+
const exitLabel = failure?.timed_out
|
|
108
|
+
? `timeout after ${failure.timeout_ms}ms`
|
|
109
|
+
: failure?.exit_code ?? failure?.signal ?? 'unknown';
|
|
110
|
+
const stderrOrError = failure?.stderr_tail || failure?.spawn_error || null;
|
|
106
111
|
|
|
107
112
|
console.log('');
|
|
108
113
|
console.log(chalk.yellow(' Run Completion Blocked By Gate Action'));
|
|
@@ -110,9 +115,9 @@ function printGateActionFailure(result, gateInfo) {
|
|
|
110
115
|
console.log('');
|
|
111
116
|
console.log(` ${chalk.dim('Gate:')} ${gateInfo.gate}`);
|
|
112
117
|
console.log(` ${chalk.dim('Action:')} ${failure?.action_label || failure?.command || '(unknown)'}`);
|
|
113
|
-
console.log(` ${chalk.dim('Exit:')} ${
|
|
114
|
-
if (
|
|
115
|
-
console.log(` ${chalk.dim('stderr:')} ${
|
|
118
|
+
console.log(` ${chalk.dim('Exit:')} ${exitLabel}`);
|
|
119
|
+
if (stderrOrError) {
|
|
120
|
+
console.log(` ${chalk.dim('stderr:')} ${stderrOrError}`);
|
|
116
121
|
}
|
|
117
122
|
console.log(` ${chalk.dim('Retry:')} agentxchain approve-completion`);
|
|
118
123
|
console.log('');
|
|
@@ -43,7 +43,8 @@ export async function approveTransitionCommand(opts) {
|
|
|
43
43
|
if (result.gate_actions?.length > 0) {
|
|
44
44
|
console.log(` ${chalk.dim('Gate actions:')} ${result.gate_actions.length}`);
|
|
45
45
|
for (const action of result.gate_actions) {
|
|
46
|
-
|
|
46
|
+
const timeoutHint = action.timeout_ms && action.timeout_ms !== 900_000 ? chalk.dim(` [timeout: ${action.timeout_ms}ms]`) : '';
|
|
47
|
+
console.log(` ${action.index}. ${action.label || action.run}${timeoutHint}`);
|
|
47
48
|
if (action.label) {
|
|
48
49
|
console.log(` ${chalk.dim(action.run)}`);
|
|
49
50
|
}
|
|
@@ -108,6 +109,10 @@ function printGateHookFailure(result, gateType, gateInfo) {
|
|
|
108
109
|
|
|
109
110
|
function printGateActionFailure(result, gateType, gateInfo) {
|
|
110
111
|
const failure = result.gateActionRun?.failed_action;
|
|
112
|
+
const exitLabel = failure?.timed_out
|
|
113
|
+
? `timeout after ${failure.timeout_ms}ms`
|
|
114
|
+
: failure?.exit_code ?? failure?.signal ?? 'unknown';
|
|
115
|
+
const stderrOrError = failure?.stderr_tail || failure?.spawn_error || null;
|
|
111
116
|
|
|
112
117
|
console.log('');
|
|
113
118
|
console.log(chalk.yellow(` ${gateType === 'phase_transition' ? 'Phase Transition' : 'Run Completion'} Blocked By Gate Action`));
|
|
@@ -119,9 +124,9 @@ function printGateActionFailure(result, gateType, gateInfo) {
|
|
|
119
124
|
}
|
|
120
125
|
console.log(` ${chalk.dim('Gate:')} ${gateInfo.gate}`);
|
|
121
126
|
console.log(` ${chalk.dim('Action:')} ${failure?.action_label || failure?.command || '(unknown)'}`);
|
|
122
|
-
console.log(` ${chalk.dim('Exit:')} ${
|
|
123
|
-
if (
|
|
124
|
-
console.log(` ${chalk.dim('stderr:')} ${
|
|
127
|
+
console.log(` ${chalk.dim('Exit:')} ${exitLabel}`);
|
|
128
|
+
if (stderrOrError) {
|
|
129
|
+
console.log(` ${chalk.dim('stderr:')} ${stderrOrError}`);
|
|
125
130
|
}
|
|
126
131
|
console.log(` ${chalk.dim('Retry:')} ${gateType === 'phase_transition' ? 'agentxchain approve-transition' : 'agentxchain approve-completion'}`);
|
|
127
132
|
console.log('');
|
package/src/commands/status.js
CHANGED
|
@@ -358,9 +358,9 @@ function renderGovernedStatus(context, opts) {
|
|
|
358
358
|
for (const action of gateActionAttempt.actions) {
|
|
359
359
|
const label = action.action_label || action.command || `action ${action.action_index || '?'}`;
|
|
360
360
|
const outcome = action.status === 'failed'
|
|
361
|
-
? chalk.red('failed')
|
|
361
|
+
? (action.timed_out ? chalk.red(`timed out after ${action.timeout_ms}ms`) : chalk.red('failed'))
|
|
362
362
|
: chalk.green('succeeded');
|
|
363
|
-
const exit = action.exit_code == null ? '' : ` (exit ${action.exit_code})
|
|
363
|
+
const exit = action.timed_out ? '' : (action.exit_code == null ? '' : ` (exit ${action.exit_code})`);
|
|
364
364
|
console.log(` ${action.action_index || '?'}. ${label} — ${outcome}${exit}`);
|
|
365
365
|
if (action.status === 'failed' && action.stderr_tail) {
|
|
366
366
|
console.log(` ${chalk.dim(action.stderr_tail)}`);
|
package/src/lib/gate-actions.js
CHANGED
|
@@ -5,6 +5,16 @@ import { spawnSync } from 'child_process';
|
|
|
5
5
|
|
|
6
6
|
const LEDGER_PATH = '.agentxchain/decision-ledger.jsonl';
|
|
7
7
|
const MAX_OUTPUT_TAIL_CHARS = 1200;
|
|
8
|
+
export const DEFAULT_GATE_ACTION_TIMEOUT_MS = 15 * 60 * 1000;
|
|
9
|
+
const MIN_GATE_ACTION_TIMEOUT_MS = 1000;
|
|
10
|
+
const MAX_GATE_ACTION_TIMEOUT_MS = 60 * 60 * 1000;
|
|
11
|
+
|
|
12
|
+
function normalizeGateActionTimeoutMs(action) {
|
|
13
|
+
if (Number.isInteger(action?.timeout_ms)) {
|
|
14
|
+
return action.timeout_ms;
|
|
15
|
+
}
|
|
16
|
+
return DEFAULT_GATE_ACTION_TIMEOUT_MS;
|
|
17
|
+
}
|
|
8
18
|
|
|
9
19
|
export function validateGateActionsConfig(gates, errors) {
|
|
10
20
|
if (!gates || typeof gates !== 'object' || Array.isArray(gates)) {
|
|
@@ -44,6 +54,15 @@ export function validateGateActionsConfig(gates, errors) {
|
|
|
44
54
|
if ('label' in action && (typeof action.label !== 'string' || !action.label.trim())) {
|
|
45
55
|
errors.push(`${prefix}.label must be a non-empty string when provided`);
|
|
46
56
|
}
|
|
57
|
+
if ('timeout_ms' in action) {
|
|
58
|
+
if (!Number.isInteger(action.timeout_ms)
|
|
59
|
+
|| action.timeout_ms < MIN_GATE_ACTION_TIMEOUT_MS
|
|
60
|
+
|| action.timeout_ms > MAX_GATE_ACTION_TIMEOUT_MS) {
|
|
61
|
+
errors.push(
|
|
62
|
+
`${prefix}.timeout_ms must be an integer between ${MIN_GATE_ACTION_TIMEOUT_MS} and ${MAX_GATE_ACTION_TIMEOUT_MS} when provided`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
47
66
|
}
|
|
48
67
|
}
|
|
49
68
|
}
|
|
@@ -60,6 +79,7 @@ export function getGateActions(config, gateId) {
|
|
|
60
79
|
index: index + 1,
|
|
61
80
|
label: typeof action.label === 'string' && action.label.trim() ? action.label.trim() : null,
|
|
62
81
|
run: action.run.trim(),
|
|
82
|
+
timeout_ms: normalizeGateActionTimeoutMs(action),
|
|
63
83
|
}));
|
|
64
84
|
}
|
|
65
85
|
|
|
@@ -78,6 +98,11 @@ function trimOutputTail(value) {
|
|
|
78
98
|
}
|
|
79
99
|
|
|
80
100
|
function buildGateActionEntry(action, meta, runtimeResult, status) {
|
|
101
|
+
const timedOut = runtimeResult?.error?.code === 'ETIMEDOUT';
|
|
102
|
+
const stderrTail = trimOutputTail(runtimeResult?.stderr) || (timedOut ? `Timed out after ${action.timeout_ms}ms` : null);
|
|
103
|
+
const spawnError = timedOut
|
|
104
|
+
? `Timed out after ${action.timeout_ms}ms`
|
|
105
|
+
: runtimeResult?.error?.message || null;
|
|
81
106
|
return {
|
|
82
107
|
type: 'gate_action',
|
|
83
108
|
timestamp: new Date().toISOString(),
|
|
@@ -90,12 +115,14 @@ function buildGateActionEntry(action, meta, runtimeResult, status) {
|
|
|
90
115
|
action_index: action.index,
|
|
91
116
|
action_label: action.label,
|
|
92
117
|
command: action.run,
|
|
118
|
+
timeout_ms: action.timeout_ms,
|
|
93
119
|
status,
|
|
94
120
|
exit_code: Number.isInteger(runtimeResult?.status) ? runtimeResult.status : null,
|
|
95
121
|
signal: runtimeResult?.signal || null,
|
|
96
122
|
stdout_tail: trimOutputTail(runtimeResult?.stdout),
|
|
97
|
-
stderr_tail:
|
|
98
|
-
spawn_error:
|
|
123
|
+
stderr_tail: stderrTail,
|
|
124
|
+
spawn_error: spawnError,
|
|
125
|
+
timed_out: timedOut,
|
|
99
126
|
};
|
|
100
127
|
}
|
|
101
128
|
|
|
@@ -125,6 +152,8 @@ export function executeGateActions(root, config, meta, opts = {}) {
|
|
|
125
152
|
},
|
|
126
153
|
encoding: 'utf8',
|
|
127
154
|
maxBuffer: 10 * 1024 * 1024,
|
|
155
|
+
timeout: action.timeout_ms,
|
|
156
|
+
killSignal: 'SIGTERM',
|
|
128
157
|
});
|
|
129
158
|
|
|
130
159
|
const status = runtimeResult.error || runtimeResult.status !== 0 ? 'failed' : 'succeeded';
|
|
@@ -170,6 +199,8 @@ export function normalizeGateActionEntry(entry) {
|
|
|
170
199
|
stdout_tail: entry.stdout_tail || null,
|
|
171
200
|
stderr_tail: entry.stderr_tail || null,
|
|
172
201
|
spawn_error: entry.spawn_error || null,
|
|
202
|
+
timeout_ms: Number.isInteger(entry.timeout_ms) ? entry.timeout_ms : null,
|
|
203
|
+
timed_out: entry.timed_out === true,
|
|
173
204
|
timestamp: entry.timestamp || null,
|
|
174
205
|
};
|
|
175
206
|
}
|
|
@@ -1281,6 +1281,8 @@ function blockRunForGateActionFailure(root, state, gateFailure, config) {
|
|
|
1281
1281
|
: 'agentxchain approve-transition';
|
|
1282
1282
|
const actionLabel = gateFailure.action_label || gateFailure.command || gateFailure.gate_id || 'gate action';
|
|
1283
1283
|
const blockedAt = gateFailure.timestamp || new Date().toISOString();
|
|
1284
|
+
const failureVerb = gateFailure.timed_out ? 'timed out' : 'failed';
|
|
1285
|
+
const failureDetail = `Gate action ${failureVerb} for "${gateFailure.gate_id || 'unknown'}": ${actionLabel}`;
|
|
1284
1286
|
const blockedState = {
|
|
1285
1287
|
...state,
|
|
1286
1288
|
status: 'blocked',
|
|
@@ -1289,13 +1291,13 @@ function blockRunForGateActionFailure(root, state, gateFailure, config) {
|
|
|
1289
1291
|
category: 'gate_action_failed',
|
|
1290
1292
|
blocked_at: blockedAt,
|
|
1291
1293
|
turn_id: gateFailure.requested_by_turn || null,
|
|
1292
|
-
detail:
|
|
1294
|
+
detail: failureDetail,
|
|
1293
1295
|
recovery: {
|
|
1294
1296
|
typed_reason: 'gate_action_failed',
|
|
1295
1297
|
owner: 'human',
|
|
1296
1298
|
recovery_action: recoveryAction,
|
|
1297
1299
|
turn_retained: false,
|
|
1298
|
-
detail: `${gateFailure.gate_id || 'unknown'} action ${gateFailure.action_index || '?'} (${actionLabel})`,
|
|
1300
|
+
detail: `${gateFailure.gate_id || 'unknown'} action ${gateFailure.action_index || '?'} (${actionLabel})${gateFailure.timed_out ? ` timed out after ${gateFailure.timeout_ms}ms` : ''}`,
|
|
1299
1301
|
},
|
|
1300
1302
|
gate_action: {
|
|
1301
1303
|
attempt_id: gateFailure.attempt_id || null,
|
|
@@ -1306,6 +1308,8 @@ function blockRunForGateActionFailure(root, state, gateFailure, config) {
|
|
|
1306
1308
|
command: gateFailure.command || null,
|
|
1307
1309
|
exit_code: gateFailure.exit_code ?? null,
|
|
1308
1310
|
stderr_tail: gateFailure.stderr_tail || null,
|
|
1311
|
+
timeout_ms: gateFailure.timeout_ms ?? null,
|
|
1312
|
+
timed_out: gateFailure.timed_out === true,
|
|
1309
1313
|
},
|
|
1310
1314
|
},
|
|
1311
1315
|
};
|
package/src/lib/report.js
CHANGED
|
@@ -1444,7 +1444,8 @@ export function formatGovernanceReportText(report) {
|
|
|
1444
1444
|
for (const action of run.gate_actions) {
|
|
1445
1445
|
const label = action.action_label || action.command || `action ${action.action_index || '?'}`;
|
|
1446
1446
|
const exit = action.exit_code == null ? 'n/a' : String(action.exit_code);
|
|
1447
|
-
|
|
1447
|
+
const timeoutTag = action.timed_out ? ` | timed_out after ${action.timeout_ms}ms` : '';
|
|
1448
|
+
lines.push(` - ${action.gate_id || 'unknown'} | ${action.gate_type || 'unknown'} | action ${action.action_index || '?'} | ${action.status} | ${label} | exit: ${exit}${timeoutTag} | at: ${action.timestamp || 'n/a'}`);
|
|
1448
1449
|
if (action.stderr_tail) {
|
|
1449
1450
|
lines.push(` stderr: ${action.stderr_tail}`);
|
|
1450
1451
|
}
|
|
@@ -2014,7 +2015,8 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
2014
2015
|
for (const action of run.gate_actions) {
|
|
2015
2016
|
const label = action.action_label || action.command || `action ${action.action_index || '?'}`;
|
|
2016
2017
|
const exit = action.exit_code == null ? 'n/a' : String(action.exit_code);
|
|
2017
|
-
|
|
2018
|
+
const mdTimeout = action.timed_out ? ` ⏱ timed out after ${action.timeout_ms}ms` : '';
|
|
2019
|
+
lines.push(`- \`${action.gate_id || 'unknown'}\` (${action.gate_type || 'unknown'}) action ${action.action_index || '?'} — **${action.status}** at \`${action.timestamp || 'n/a'}\`: ${label} (exit \`${exit}\`)${mdTimeout}`);
|
|
2018
2020
|
if (action.stderr_tail) {
|
|
2019
2021
|
lines.push(` - stderr: ${action.stderr_tail}`);
|
|
2020
2022
|
}
|
|
@@ -2723,7 +2725,8 @@ function renderRunHtml(report) {
|
|
|
2723
2725
|
for (const action of run.gate_actions) {
|
|
2724
2726
|
const label = action.action_label || action.command || `action ${action.action_index || '?'}`;
|
|
2725
2727
|
const exit = action.exit_code == null ? 'n/a' : String(action.exit_code);
|
|
2726
|
-
|
|
2728
|
+
const htmlTimeout = action.timed_out ? ` <em>⏱ timed out after ${esc(String(action.timeout_ms))}ms</em>` : '';
|
|
2729
|
+
gaHtml += `<li><code>${esc(action.gate_id || 'unknown')}</code> (${esc(action.gate_type || 'unknown')}) action ${esc(String(action.action_index || '?'))} — <strong>${esc(action.status)}</strong> at <code>${esc(action.timestamp || 'n/a')}</code>: ${esc(label)} (exit <code>${esc(exit)}</code>)${htmlTimeout}`;
|
|
2727
2730
|
if (action.stderr_tail) {
|
|
2728
2731
|
gaHtml += `<br><code>${esc(action.stderr_tail)}</code>`;
|
|
2729
2732
|
}
|