agentxchain 2.154.10 → 2.155.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/init.js +24 -0
- package/src/commands/run.js +14 -2
- package/src/commands/schedule.js +29 -12
- package/src/lib/continuous-run.js +534 -14
- package/src/lib/governed-state.js +59 -0
- package/src/lib/idle-expansion-result-validator.js +251 -0
- package/src/lib/intake.js +85 -1
- package/src/lib/normalized-config.js +64 -0
- package/src/lib/schemas/agentxchain-config.schema.json +31 -0
- package/src/lib/schemas/turn-result.schema.json +67 -0
- package/src/lib/turn-result-validator.js +25 -0
- package/src/lib/vision-reader.js +165 -1
package/bin/agentxchain.js
CHANGED
|
@@ -759,6 +759,7 @@ program
|
|
|
759
759
|
.option('--poll-seconds <n>', 'Seconds between idle-detection cycles in continuous mode (default: 30)', parseInt)
|
|
760
760
|
.option('--triage-approval <mode>', 'Triage policy for vision-derived intents: auto or human (default: config or auto)')
|
|
761
761
|
.option('--max-idle-cycles <n>', 'Stop after N consecutive idle cycles with no derivable work (default: 3)', parseInt)
|
|
762
|
+
.option('--on-idle <mode>', 'Continuous idle policy: exit, perpetual, or human_review (default: config or exit)')
|
|
762
763
|
.option('--session-budget <usd>', 'Cumulative session-level budget cap in USD for continuous mode', parseFloat)
|
|
763
764
|
.option('--auto-retry-on-ghost', 'Enable bounded automatic retry for continuous-mode startup ghost turns')
|
|
764
765
|
.option('--no-auto-retry-on-ghost', 'Disable bounded automatic retry for continuous-mode startup ghost turns')
|
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -12,6 +12,29 @@ import { buildGovernedPlanningArtifacts, interpolateTemplateContent } from '../l
|
|
|
12
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
13
|
const TEMPLATES_DIR = join(__dirname, '../templates');
|
|
14
14
|
|
|
15
|
+
const PM_IDLE_EXPANSION_PROMPT = `# Idle-Expansion Charter
|
|
16
|
+
|
|
17
|
+
You are running in IDLE-EXPANSION mode. The continuous loop found no directly derivable work after the configured idle threshold. Your task is to decide the next governed product increment from the configured sources, or to declare the product vision exhausted.
|
|
18
|
+
|
|
19
|
+
Read these sources in order:
|
|
20
|
+
|
|
21
|
+
1. \`.planning/VISION.md\` (read-only; never modify)
|
|
22
|
+
2. \`.planning/ROADMAP.md\`
|
|
23
|
+
3. \`.planning/SYSTEM_SPEC.md\`
|
|
24
|
+
4. \`.agentxchain/intake/\`
|
|
25
|
+
5. \`.planning/acceptance-matrix.md\` when present
|
|
26
|
+
|
|
27
|
+
You must emit exactly one structured \`idle_expansion_result\` in the turn result:
|
|
28
|
+
|
|
29
|
+
1. \`kind: "new_intake_intent"\`
|
|
30
|
+
Produce one concrete intake intent with \`title\`, \`priority\`, \`template\`, \`charter\`, \`acceptance_contract\`, and \`vision_traceability\`. The intent must cite at least one existing \`VISION.md\` heading or goal it advances. Do not invent work outside the human-owned vision.
|
|
31
|
+
|
|
32
|
+
2. \`kind: "vision_exhausted"\`
|
|
33
|
+
Classify every top-level \`VISION.md\` heading as \`complete\`, \`deferred\`, or \`out_of_scope\`, with a reason for each. Use this only when no next governed product increment can be justified from the configured sources.
|
|
34
|
+
|
|
35
|
+
Do not modify \`.planning/VISION.md\`. If you cannot cite \`VISION.md\` for a proposed intent, return \`vision_exhausted\` or a deferred classification instead of expanding scope.
|
|
36
|
+
`;
|
|
37
|
+
|
|
15
38
|
const DEFAULT_AGENTS = {
|
|
16
39
|
pm: {
|
|
17
40
|
name: 'Product Manager',
|
|
@@ -906,6 +929,7 @@ export function scaffoldGoverned(dir, projectName, projectId, templateId = 'gene
|
|
|
906
929
|
const prompt = appendPromptOverride(basePrompt, template.prompt_overrides?.[roleId]);
|
|
907
930
|
writeFileSync(join(dir, '.agentxchain', 'prompts', `${roleId}.md`), prompt);
|
|
908
931
|
}
|
|
932
|
+
writeFileSync(join(dir, '.agentxchain', 'prompts', 'pm-idle-expansion.md'), PM_IDLE_EXPANSION_PROMPT);
|
|
909
933
|
|
|
910
934
|
// Planning artifacts
|
|
911
935
|
for (const artifact of buildGovernedPlanningArtifacts({
|
package/src/commands/run.js
CHANGED
|
@@ -258,6 +258,7 @@ export async function executeGovernedRun(context, opts = {}) {
|
|
|
258
258
|
// ── Track first-call for --role override ────────────────────────────────
|
|
259
259
|
let firstSelectRole = true;
|
|
260
260
|
let qaMissingCredentialsFallback = null;
|
|
261
|
+
const acceptedTurnResults = [];
|
|
261
262
|
|
|
262
263
|
// ── Callbacks ───────────────────────────────────────────────────────────
|
|
263
264
|
const callbacks = {
|
|
@@ -573,7 +574,15 @@ export async function executeGovernedRun(context, opts = {}) {
|
|
|
573
574
|
return approved;
|
|
574
575
|
},
|
|
575
576
|
|
|
576
|
-
async afterAccept({ turn }) {
|
|
577
|
+
async afterAccept({ turn, acceptResult }) {
|
|
578
|
+
if (acceptResult) {
|
|
579
|
+
acceptedTurnResults.push({
|
|
580
|
+
turn_id: turn.turn_id,
|
|
581
|
+
accepted: acceptResult.accepted || null,
|
|
582
|
+
turn_result: acceptResult.validation?.turnResult || null,
|
|
583
|
+
state: acceptResult.state || null,
|
|
584
|
+
});
|
|
585
|
+
}
|
|
577
586
|
if (!autoCheckpoint) {
|
|
578
587
|
return { ok: true };
|
|
579
588
|
}
|
|
@@ -688,7 +697,10 @@ export async function executeGovernedRun(context, opts = {}) {
|
|
|
688
697
|
const successReasons = new Set(['completed', 'gate_held', 'caller_stopped', 'max_turns_reached']);
|
|
689
698
|
return {
|
|
690
699
|
exitCode: result.ok || successReasons.has(result.stop_reason) ? 0 : 1,
|
|
691
|
-
result
|
|
700
|
+
result: {
|
|
701
|
+
...result,
|
|
702
|
+
accepted_turn_results: acceptedTurnResults,
|
|
703
|
+
},
|
|
692
704
|
skipped: false,
|
|
693
705
|
skipReason: null,
|
|
694
706
|
provenance: provenance || null,
|
package/src/commands/schedule.js
CHANGED
|
@@ -21,8 +21,8 @@ import {
|
|
|
21
21
|
advanceContinuousRunOnce,
|
|
22
22
|
resolveContinuousOptions,
|
|
23
23
|
} from '../lib/continuous-run.js';
|
|
24
|
-
import { resolveVisionPath } from '../lib/vision-reader.js';
|
|
25
|
-
import { existsSync } from 'node:fs';
|
|
24
|
+
import { captureVisionHeadingsSnapshot, computeVisionContentSha, resolveVisionPath } from '../lib/vision-reader.js';
|
|
25
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
26
26
|
import { randomUUID } from 'node:crypto';
|
|
27
27
|
|
|
28
28
|
function loadScheduleContext() {
|
|
@@ -349,7 +349,7 @@ async function runDueSchedules(context, opts = {}) {
|
|
|
349
349
|
// ---------------------------------------------------------------------------
|
|
350
350
|
|
|
351
351
|
function isSessionTerminal(session) {
|
|
352
|
-
return ['completed', 'idle_exit', 'failed', 'stopped'].includes(session?.status);
|
|
352
|
+
return ['completed', 'idle_exit', 'failed', 'stopped', 'vision_exhausted', 'vision_expansion_exhausted'].includes(session?.status);
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
function getContinuousEnabledScheduleIds(config) {
|
|
@@ -393,7 +393,7 @@ export function selectContinuousScheduleEntry(root, config, opts = {}) {
|
|
|
393
393
|
return { id: dueEntry.id, schedule: config.schedules[dueEntry.id], due: dueEntry.due };
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
function createScheduleOwnedSession(schedule, scheduleId) {
|
|
396
|
+
function createScheduleOwnedSession(schedule, scheduleId, snapshotOpts = {}) {
|
|
397
397
|
return {
|
|
398
398
|
session_id: `cont-${randomUUID().slice(0, 8)}`,
|
|
399
399
|
started_at: new Date().toISOString(),
|
|
@@ -410,6 +410,10 @@ function createScheduleOwnedSession(schedule, scheduleId) {
|
|
|
410
410
|
per_session_max_usd: schedule.continuous.per_session_max_usd || null,
|
|
411
411
|
cumulative_spent_usd: 0,
|
|
412
412
|
budget_exhausted: false,
|
|
413
|
+
vision_headings_snapshot: snapshotOpts.visionHeadingsSnapshot || null,
|
|
414
|
+
vision_sha_at_snapshot: snapshotOpts.visionShaAtSnapshot || null,
|
|
415
|
+
expansion_iteration: 0,
|
|
416
|
+
_vision_stale_warned_shas: [],
|
|
413
417
|
};
|
|
414
418
|
}
|
|
415
419
|
|
|
@@ -453,7 +457,11 @@ async function advanceScheduleContinuousSession(context, entry, opts = {}) {
|
|
|
453
457
|
return { ok: false, action: 'failed', reason: `VISION.md not found at ${absVision}` };
|
|
454
458
|
}
|
|
455
459
|
|
|
456
|
-
|
|
460
|
+
const visionContent = readFileSync(absVision, 'utf8');
|
|
461
|
+
session = createScheduleOwnedSession(schedule, scheduleId, {
|
|
462
|
+
visionHeadingsSnapshot: captureVisionHeadingsSnapshot(visionContent),
|
|
463
|
+
visionShaAtSnapshot: computeVisionContentSha(visionContent),
|
|
464
|
+
});
|
|
457
465
|
writeContinuousSession(root, session);
|
|
458
466
|
log(chalk.cyan(`Started schedule-owned continuous session: ${session.session_id} (schedule: ${scheduleId})`));
|
|
459
467
|
|
|
@@ -467,13 +475,17 @@ async function advanceScheduleContinuousSession(context, entry, opts = {}) {
|
|
|
467
475
|
}
|
|
468
476
|
|
|
469
477
|
// Build contOpts from schedule continuous config
|
|
470
|
-
const contOpts = {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
478
|
+
const contOpts = resolveContinuousOptions({ continuous: true }, {
|
|
479
|
+
...config,
|
|
480
|
+
run_loop: {
|
|
481
|
+
...(config.run_loop || {}),
|
|
482
|
+
continuous: {
|
|
483
|
+
...(config.run_loop?.continuous || {}),
|
|
484
|
+
...contConfig,
|
|
485
|
+
enabled: true,
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
});
|
|
477
489
|
|
|
478
490
|
// Advance one step
|
|
479
491
|
const step = await advanceContinuousRunOnce(context, session, contOpts, executeGovernedRun, log);
|
|
@@ -484,9 +496,14 @@ async function advanceScheduleContinuousSession(context, entry, opts = {}) {
|
|
|
484
496
|
idle_exit: 'continuous_idle_exit',
|
|
485
497
|
failed: 'continuous_failed',
|
|
486
498
|
blocked: 'continuous_blocked',
|
|
499
|
+
vision_exhausted: 'continuous_vision_exhausted',
|
|
500
|
+
vision_expansion_exhausted: 'continuous_vision_expansion_exhausted',
|
|
487
501
|
running: 'continuous_running',
|
|
488
502
|
};
|
|
489
503
|
let schedStatus = statusMap[step.status] || 'continuous_running';
|
|
504
|
+
if (step.action === 'idle_expansion_dispatched') {
|
|
505
|
+
schedStatus = 'continuous_running';
|
|
506
|
+
}
|
|
490
507
|
if (step.action === 'session_budget_exhausted') {
|
|
491
508
|
schedStatus = 'continuous_session_budget_exhausted';
|
|
492
509
|
}
|