agentxchain 2.138.0 → 2.138.1
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
|
@@ -24,6 +24,14 @@ export async function intakeApproveCommand(opts) {
|
|
|
24
24
|
console.log(JSON.stringify(result, null, 2));
|
|
25
25
|
} else if (result.ok) {
|
|
26
26
|
console.log('');
|
|
27
|
+
if (result.superseded) {
|
|
28
|
+
console.log(chalk.yellow(` Superseded intent ${result.intent.intent_id}`));
|
|
29
|
+
console.log(chalk.dim(` Approver: ${result.intent.approved_by}`));
|
|
30
|
+
console.log(chalk.dim(` Status: ${result.intent.history.at(-2)?.to || 'triaged'} → superseded`));
|
|
31
|
+
console.log(chalk.dim(` Reason: ${result.intent.archived_reason}`));
|
|
32
|
+
console.log('');
|
|
33
|
+
process.exit(result.exitCode);
|
|
34
|
+
}
|
|
27
35
|
console.log(chalk.green(` Approved intent ${result.intent.intent_id}`));
|
|
28
36
|
console.log(chalk.dim(` Approver: ${result.intent.approved_by}`));
|
|
29
37
|
console.log(chalk.dim(` Status: triaged → approved`));
|
package/src/lib/intake.js
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
archiveStaleIntentsForRun,
|
|
22
22
|
migratePreBug34Intents,
|
|
23
23
|
formatLegacyIntentMigrationNotice,
|
|
24
|
+
isPhantomIntent,
|
|
24
25
|
} from './intent-startup-migration.js';
|
|
25
26
|
|
|
26
27
|
const VALID_SOURCES = ['manual', 'ci_failure', 'git_ref_change', 'schedule', 'vision_scan'];
|
|
@@ -807,6 +808,16 @@ export function approveIntent(root, intentId, options = {}) {
|
|
|
807
808
|
intent.status = 'approved';
|
|
808
809
|
intent.approved_by = approver;
|
|
809
810
|
intent.updated_at = now;
|
|
811
|
+
|
|
812
|
+
const phantomReason = 'planning artifacts for this intent already exist on disk; intent superseded during approval';
|
|
813
|
+
if (intent.approved_run_id && isPhantomIntent(root, intent)) {
|
|
814
|
+
intent.status = 'superseded';
|
|
815
|
+
intent.archived_reason = phantomReason;
|
|
816
|
+
intent.history.push({ from: previousStatus, to: 'superseded', at: now, reason: phantomReason, approver });
|
|
817
|
+
safeWriteJson(intentPath, intent);
|
|
818
|
+
return { ok: true, intent, superseded: true, exitCode: 0 };
|
|
819
|
+
}
|
|
820
|
+
|
|
810
821
|
intent.history.push({ from: previousStatus, to: 'approved', at: now, reason, approver });
|
|
811
822
|
|
|
812
823
|
safeWriteJson(intentPath, intent);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
|
|
4
|
+
import { queryAcceptedTurnHistory } from './accepted-turn-history.js';
|
|
4
5
|
import { safeWriteJson } from './safe-write.js';
|
|
5
6
|
import { VALID_GOVERNED_TEMPLATE_IDS, loadGovernedTemplate } from './governed-templates.js';
|
|
6
7
|
|
|
@@ -28,7 +29,37 @@ function normalizeArtifactPaths(paths) {
|
|
|
28
29
|
)];
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
function
|
|
32
|
+
function parseTimestamp(value) {
|
|
33
|
+
if (typeof value !== 'string' || !value.trim()) return null;
|
|
34
|
+
const parsed = Date.parse(value);
|
|
35
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function hasPlanningHistoryEvidence(root, intent) {
|
|
39
|
+
const intentId = intent?.intent_id || null;
|
|
40
|
+
const runId = intent?.approved_run_id || null;
|
|
41
|
+
const intentTimestamp = parseTimestamp(intent?.approved_at)
|
|
42
|
+
?? parseTimestamp(intent?.created_at)
|
|
43
|
+
?? parseTimestamp(intent?.updated_at);
|
|
44
|
+
|
|
45
|
+
for (const entry of queryAcceptedTurnHistory(root)) {
|
|
46
|
+
if (entry?.phase !== 'planning') continue;
|
|
47
|
+
|
|
48
|
+
if (intentId && entry.intent_id === intentId) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!runId || entry.run_id !== runId || intentTimestamp === null) continue;
|
|
53
|
+
const acceptedAt = parseTimestamp(entry.accepted_at);
|
|
54
|
+
if (acceptedAt !== null && acceptedAt >= intentTimestamp) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readPlanningGateFiles(root, intent) {
|
|
32
63
|
const configPath = join(root, 'agentxchain.json');
|
|
33
64
|
if (!existsSync(configPath)) return [];
|
|
34
65
|
|
|
@@ -39,27 +70,10 @@ function readPlanningGateFiles(root) {
|
|
|
39
70
|
return [];
|
|
40
71
|
}
|
|
41
72
|
|
|
42
|
-
// Only use planning gate requires_files for phantom detection when:
|
|
43
|
-
// 1. The planning gate has NOT been passed yet (once passed, these files
|
|
44
|
-
// are expected to exist from normal planning work), AND
|
|
45
|
-
// 2. At least one turn has been completed (turn_sequence > 0). If no turns
|
|
46
|
-
// have been completed, the files are scaffolding templates, not evidence
|
|
47
|
-
// of completed planning work. Without this check, ANY approved intent
|
|
48
|
-
// in a freshly scaffolded project would be falsely detected as phantom.
|
|
49
|
-
const statePath = join(root, '.agentxchain', 'state.json');
|
|
50
|
-
try {
|
|
51
|
-
const state = JSON.parse(readFileSync(statePath, 'utf8'));
|
|
52
|
-
const gateStatus = state.phase_gate_status || {};
|
|
53
|
-
const exitGateId = config?.routing?.planning?.exit_gate;
|
|
54
|
-
if (exitGateId && gateStatus[exitGateId] === 'passed') return [];
|
|
55
|
-
const turnSequence = state.turn_sequence || 0;
|
|
56
|
-
if (turnSequence === 0) return [];
|
|
57
|
-
} catch {
|
|
58
|
-
// If state is unreadable, fall through to check gate files
|
|
59
|
-
}
|
|
60
|
-
|
|
61
73
|
const exitGateId = config?.routing?.planning?.exit_gate;
|
|
62
74
|
const requiresFiles = exitGateId ? config?.gates?.[exitGateId]?.requires_files : null;
|
|
75
|
+
if (!Array.isArray(requiresFiles) || requiresFiles.length === 0) return [];
|
|
76
|
+
if (!hasPlanningHistoryEvidence(root, intent)) return [];
|
|
63
77
|
return normalizeArtifactPaths(requiresFiles);
|
|
64
78
|
}
|
|
65
79
|
|
|
@@ -146,7 +160,7 @@ export function listExpectedPlanningArtifacts(root, intent) {
|
|
|
146
160
|
return normalizeArtifactPaths([
|
|
147
161
|
...recordedArtifacts,
|
|
148
162
|
...templateArtifacts,
|
|
149
|
-
...readPlanningGateFiles(root),
|
|
163
|
+
...readPlanningGateFiles(root, intent),
|
|
150
164
|
]);
|
|
151
165
|
}
|
|
152
166
|
|