agentxchain 2.129.0 → 2.130.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 +10 -0
- package/package.json +1 -1
- package/src/commands/accept-turn.js +14 -0
- package/src/commands/checkpoint-turn.js +35 -0
- package/src/commands/doctor.js +31 -2
- package/src/commands/mission.js +661 -7
- package/src/commands/reject-turn.js +36 -2
- package/src/commands/restart.js +72 -8
- package/src/commands/run.js +13 -0
- package/src/commands/status.js +13 -1
- package/src/lib/continuous-run.js +8 -1
- package/src/lib/coordinator-dispatch.js +25 -0
- package/src/lib/governed-state.js +150 -1
- package/src/lib/intake.js +12 -0
- package/src/lib/mission-plans.js +510 -6
- package/src/lib/missions.js +9 -2
- package/src/lib/repo-observer.js +1 -0
- package/src/lib/run-events.js +1 -0
- package/src/lib/run-loop.js +20 -0
- package/src/lib/turn-checkpoint.js +221 -0
package/bin/agentxchain.js
CHANGED
|
@@ -71,6 +71,7 @@ import { unblockCommand } from '../src/commands/unblock.js';
|
|
|
71
71
|
import { injectCommand } from '../src/commands/inject.js';
|
|
72
72
|
import { escalateCommand } from '../src/commands/escalate.js';
|
|
73
73
|
import { acceptTurnCommand } from '../src/commands/accept-turn.js';
|
|
74
|
+
import { checkpointTurnCommand } from '../src/commands/checkpoint-turn.js';
|
|
74
75
|
import { rejectTurnCommand } from '../src/commands/reject-turn.js';
|
|
75
76
|
import { reissueTurnCommand } from '../src/commands/reissue-turn.js';
|
|
76
77
|
import { proposalListCommand, proposalDiffCommand, proposalApplyCommand, proposalRejectCommand } from '../src/commands/proposal.js';
|
|
@@ -672,9 +673,16 @@ program
|
|
|
672
673
|
.command('accept-turn')
|
|
673
674
|
.description('Accept the currently staged governed turn result')
|
|
674
675
|
.option('--turn <id>', 'Target a specific active turn when multiple turns exist')
|
|
676
|
+
.option('--checkpoint', 'Checkpoint the accepted turn to git immediately after acceptance')
|
|
675
677
|
.option('--resolution <mode>', 'Conflict resolution mode for conflicted turns (standard, human_merge)', 'standard')
|
|
676
678
|
.action(acceptTurnCommand);
|
|
677
679
|
|
|
680
|
+
program
|
|
681
|
+
.command('checkpoint-turn')
|
|
682
|
+
.description('Checkpoint the latest accepted turn into git so the next writable turn has a clean baseline')
|
|
683
|
+
.option('--turn <id>', 'Checkpoint a specific accepted turn from history')
|
|
684
|
+
.action(checkpointTurnCommand);
|
|
685
|
+
|
|
678
686
|
program
|
|
679
687
|
.command('reject-turn')
|
|
680
688
|
.description('Reject the current governed turn result and retry or escalate')
|
|
@@ -727,6 +735,8 @@ program
|
|
|
727
735
|
.option('--triage-approval <mode>', 'Triage policy for vision-derived intents: auto or human (default: config or auto)')
|
|
728
736
|
.option('--max-idle-cycles <n>', 'Stop after N consecutive idle cycles with no derivable work (default: 3)', parseInt)
|
|
729
737
|
.option('--session-budget <usd>', 'Cumulative session-level budget cap in USD for continuous mode', parseFloat)
|
|
738
|
+
.option('--auto-checkpoint', 'Auto-commit accepted writable turns after acceptance')
|
|
739
|
+
.option('--no-auto-checkpoint', 'Disable automatic checkpointing after accepted writable turns')
|
|
730
740
|
.action(runCommand);
|
|
731
741
|
|
|
732
742
|
program
|
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@ import chalk from 'chalk';
|
|
|
2
2
|
import { loadProjectContext } from '../lib/config.js';
|
|
3
3
|
import { acceptGovernedTurn } from '../lib/governed-state.js';
|
|
4
4
|
import { deriveRecoveryDescriptor } from '../lib/blocked-state.js';
|
|
5
|
+
import { checkpointAcceptedTurn } from '../lib/turn-checkpoint.js';
|
|
5
6
|
|
|
6
7
|
export async function acceptTurnCommand(opts = {}) {
|
|
7
8
|
const context = loadProjectContext();
|
|
@@ -166,6 +167,19 @@ export async function acceptTurnCommand(opts = {}) {
|
|
|
166
167
|
if (accepted?.cost?.usd != null) {
|
|
167
168
|
console.log(` ${chalk.dim('Cost:')} $${formatUsd(accepted.cost.usd)}`);
|
|
168
169
|
}
|
|
170
|
+
if (opts.checkpoint) {
|
|
171
|
+
const checkpoint = checkpointAcceptedTurn(root, { turnId });
|
|
172
|
+
if (!checkpoint.ok) {
|
|
173
|
+
console.log(` ${chalk.yellow('Checkpoint:')} accepted but checkpoint failed`);
|
|
174
|
+
console.log(` ${chalk.dim('Action:')} ${checkpoint.error}`);
|
|
175
|
+
console.log(` ${chalk.dim('Retry:')} agentxchain checkpoint-turn --turn ${turnId}`);
|
|
176
|
+
console.log('');
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
if (!checkpoint.skipped) {
|
|
180
|
+
console.log(` ${chalk.dim('Checkpoint:')} ${checkpoint.checkpoint_sha}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
169
183
|
if (accepted?.verification_replay) {
|
|
170
184
|
const verifiedAt = accepted.verification_replay.verified_at
|
|
171
185
|
? ` at ${accepted.verification_replay.verified_at}`
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { loadProjectContext } from '../lib/config.js';
|
|
3
|
+
import { checkpointAcceptedTurn } from '../lib/turn-checkpoint.js';
|
|
4
|
+
|
|
5
|
+
export async function checkpointTurnCommand(opts = {}) {
|
|
6
|
+
const context = loadProjectContext();
|
|
7
|
+
if (!context) {
|
|
8
|
+
console.log(chalk.red('No agentxchain.json found. Run `agentxchain init` first.'));
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { root, config } = context;
|
|
13
|
+
if (config.protocol_mode !== 'governed') {
|
|
14
|
+
console.log(chalk.red('The checkpoint-turn command is only available for governed projects.'));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const result = checkpointAcceptedTurn(root, { turnId: opts.turn });
|
|
19
|
+
if (!result.ok) {
|
|
20
|
+
console.log(chalk.red(result.error || 'Failed to checkpoint accepted turn.'));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (result.already_checkpointed) {
|
|
25
|
+
console.log(chalk.yellow(`Turn ${result.turn.turn_id} already has checkpoint ${result.checkpoint_sha}.`));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (result.skipped) {
|
|
30
|
+
console.log(chalk.dim(result.reason));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log(chalk.green(`Checkpointed ${result.turn.turn_id} at ${result.checkpoint_sha}.`));
|
|
35
|
+
}
|
package/src/commands/doctor.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
summarizeRoleRuntimeCapability,
|
|
18
18
|
summarizeRuntimeCapabilityContract,
|
|
19
19
|
} from '../lib/runtime-capabilities.js';
|
|
20
|
-
import { detectActiveTurnBindingDrift } from '../lib/governed-state.js';
|
|
20
|
+
import { detectActiveTurnBindingDrift, detectStateBundleDesync } from '../lib/governed-state.js';
|
|
21
21
|
import { findPendingApprovedIntents } from '../lib/intake.js';
|
|
22
22
|
import { checkCleanBaseline } from '../lib/repo-observer.js';
|
|
23
23
|
|
|
@@ -152,7 +152,36 @@ function governedDoctor(root, rawConfig, opts) {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
// 5c.
|
|
155
|
+
// 5c. BUG-18: State/bundle integrity — active turns must have dispatch bundles
|
|
156
|
+
if (normalized && existsSync(join(root, '.agentxchain', 'state.json'))) {
|
|
157
|
+
try {
|
|
158
|
+
const stateData = loadProjectState(root, normalized);
|
|
159
|
+
const desync = detectStateBundleDesync(root, stateData);
|
|
160
|
+
if (!desync.ok) {
|
|
161
|
+
const desyncSummary = desync.desynced
|
|
162
|
+
.map(d => `${d.turn_id} (${d.role}): missing ${d.expected_path}`)
|
|
163
|
+
.join('; ');
|
|
164
|
+
checks.push({
|
|
165
|
+
id: 'bundle_integrity',
|
|
166
|
+
name: 'Dispatch bundle integrity',
|
|
167
|
+
level: 'fail',
|
|
168
|
+
detail: `Ghost turn(s): ${desyncSummary}. Run: agentxchain reissue-turn`,
|
|
169
|
+
desynced: desync.desynced,
|
|
170
|
+
});
|
|
171
|
+
} else if (Object.keys(stateData?.active_turns || {}).length > 0) {
|
|
172
|
+
checks.push({
|
|
173
|
+
id: 'bundle_integrity',
|
|
174
|
+
name: 'Dispatch bundle integrity',
|
|
175
|
+
level: 'pass',
|
|
176
|
+
detail: 'All active turns have dispatch bundles on disk',
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
// State couldn't be loaded — skip integrity check
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 5d. Clean working tree pre-flight for authoritative/proposed roles
|
|
156
185
|
if (normalized?.roles) {
|
|
157
186
|
const writableRoles = Object.entries(normalized.roles)
|
|
158
187
|
.filter(([, role]) => role.write_authority === 'authoritative' || role.write_authority === 'proposed')
|