substrate-ai 0.2.10 → 0.2.13
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/dist/cli/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createLogger, deepMask } from "../logger-C6n1g8uP.js";
|
|
3
3
|
import { AdapterRegistry, createEventBus } from "../event-bus-J-bw-pkp.js";
|
|
4
4
|
import { CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, PartialSubstrateConfigSchema, SUPPORTED_CONFIG_FORMAT_VERSIONS, SubstrateConfigSchema, defaultConfigMigrator } from "../version-manager-impl-BpVx2DkY.js";
|
|
5
|
-
import { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getSubstrateDefaultSettings, parseDbTimestampAsUtc, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-
|
|
5
|
+
import { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getSubstrateDefaultSettings, parseDbTimestampAsUtc, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-CoP8UQU3.js";
|
|
6
6
|
import { ConfigError, ConfigIncompatibleFormatError } from "../errors-BPqtzQ4U.js";
|
|
7
7
|
import { addTokenUsage, createDecision, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getTokenUsageSummary, listRequirements, updatePipelineRun } from "../decisions-DNYByk0U.js";
|
|
8
8
|
import { aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../metrics-BSg8VIHd.js";
|
|
@@ -2414,16 +2414,24 @@ const DEFAULT_STALL_THRESHOLD_SECONDS = 600;
|
|
|
2414
2414
|
* - `node dist/cli/index.js run` (npm run substrate:dev)
|
|
2415
2415
|
* - `npx substrate run`
|
|
2416
2416
|
* - any node process whose command contains `run` with `--events` or `--stories`
|
|
2417
|
+
*
|
|
2418
|
+
* When `projectRoot` is provided, additionally checks that the command line
|
|
2419
|
+
* contains that path (via `--project-root` flag or as part of the binary/CWD path).
|
|
2420
|
+
* This ensures multi-project environments match the correct orchestrator.
|
|
2417
2421
|
*/
|
|
2418
|
-
function isOrchestratorProcessLine(line) {
|
|
2422
|
+
function isOrchestratorProcessLine(line, projectRoot) {
|
|
2419
2423
|
if (line.includes("grep")) return false;
|
|
2420
|
-
|
|
2421
|
-
if (line.includes("substrate
|
|
2422
|
-
if (line.includes("
|
|
2423
|
-
if (line.includes("
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2424
|
+
let isOrchestrator = false;
|
|
2425
|
+
if (line.includes("substrate run")) isOrchestrator = true;
|
|
2426
|
+
else if (line.includes("substrate-ai run")) isOrchestrator = true;
|
|
2427
|
+
else if (line.includes("index.js run")) isOrchestrator = true;
|
|
2428
|
+
else if (line.includes("node") && /\srun(\s|$)/.test(line) && (line.includes("--events") || line.includes("--stories"))) isOrchestrator = true;
|
|
2429
|
+
if (!isOrchestrator) return false;
|
|
2430
|
+
if (projectRoot !== void 0) return line.includes(projectRoot);
|
|
2431
|
+
return true;
|
|
2432
|
+
}
|
|
2433
|
+
function inspectProcessTree(opts) {
|
|
2434
|
+
const { projectRoot, execFileSync: execFileSyncOverride } = opts ?? {};
|
|
2427
2435
|
const result = {
|
|
2428
2436
|
orchestrator_pid: null,
|
|
2429
2437
|
child_pids: [],
|
|
@@ -2443,7 +2451,7 @@ function inspectProcessTree(execFileSyncOverride) {
|
|
|
2443
2451
|
});
|
|
2444
2452
|
}
|
|
2445
2453
|
const lines = psOutput.split("\n");
|
|
2446
|
-
for (const line of lines) if (isOrchestratorProcessLine(line)) {
|
|
2454
|
+
for (const line of lines) if (isOrchestratorProcessLine(line, projectRoot)) {
|
|
2447
2455
|
const match = line.trim().match(/^(\d+)/);
|
|
2448
2456
|
if (match) {
|
|
2449
2457
|
result.orchestrator_pid = parseInt(match[1], 10);
|
|
@@ -2466,6 +2474,58 @@ function inspectProcessTree(execFileSyncOverride) {
|
|
|
2466
2474
|
return result;
|
|
2467
2475
|
}
|
|
2468
2476
|
/**
|
|
2477
|
+
* Collect all descendant PIDs of the given root PIDs by walking the process
|
|
2478
|
+
* tree recursively. This ensures that grandchildren of the orchestrator
|
|
2479
|
+
* (e.g. node subprocesses spawned by `claude -p`) are also killed during
|
|
2480
|
+
* stall recovery, leaving no orphan processes.
|
|
2481
|
+
*
|
|
2482
|
+
* Returns only the descendants — the root PIDs themselves are NOT included.
|
|
2483
|
+
*/
|
|
2484
|
+
function getAllDescendantPids(rootPids, execFileSyncOverride) {
|
|
2485
|
+
if (rootPids.length === 0) return [];
|
|
2486
|
+
try {
|
|
2487
|
+
let psOutput;
|
|
2488
|
+
if (execFileSyncOverride !== void 0) psOutput = execFileSyncOverride("ps", ["-eo", "pid,ppid"], {
|
|
2489
|
+
encoding: "utf-8",
|
|
2490
|
+
timeout: 5e3
|
|
2491
|
+
});
|
|
2492
|
+
else {
|
|
2493
|
+
const { execFileSync } = __require("node:child_process");
|
|
2494
|
+
psOutput = execFileSync("ps", ["-eo", "pid,ppid"], {
|
|
2495
|
+
encoding: "utf-8",
|
|
2496
|
+
timeout: 5e3
|
|
2497
|
+
});
|
|
2498
|
+
}
|
|
2499
|
+
const childrenOf = new Map();
|
|
2500
|
+
for (const line of psOutput.split("\n")) {
|
|
2501
|
+
const parts = line.trim().split(/\s+/);
|
|
2502
|
+
if (parts.length >= 2) {
|
|
2503
|
+
const pid = parseInt(parts[0], 10);
|
|
2504
|
+
const ppid = parseInt(parts[1], 10);
|
|
2505
|
+
if (!isNaN(pid) && !isNaN(ppid) && pid > 0) {
|
|
2506
|
+
if (!childrenOf.has(ppid)) childrenOf.set(ppid, []);
|
|
2507
|
+
childrenOf.get(ppid).push(pid);
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
const descendants = [];
|
|
2512
|
+
const seen = new Set(rootPids);
|
|
2513
|
+
const queue = [...rootPids];
|
|
2514
|
+
while (queue.length > 0) {
|
|
2515
|
+
const current = queue.shift();
|
|
2516
|
+
const children = childrenOf.get(current) ?? [];
|
|
2517
|
+
for (const child of children) if (!seen.has(child)) {
|
|
2518
|
+
seen.add(child);
|
|
2519
|
+
descendants.push(child);
|
|
2520
|
+
queue.push(child);
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
return descendants;
|
|
2524
|
+
} catch {
|
|
2525
|
+
return [];
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
/**
|
|
2469
2529
|
* Fetch pipeline health data as a structured object without any stdout side-effects.
|
|
2470
2530
|
* Used by runSupervisorAction to poll health without formatting overhead.
|
|
2471
2531
|
*
|
|
@@ -2524,10 +2584,11 @@ async function getAutoHealthData(options) {
|
|
|
2524
2584
|
}
|
|
2525
2585
|
}
|
|
2526
2586
|
} catch {}
|
|
2527
|
-
const processInfo = inspectProcessTree();
|
|
2587
|
+
const processInfo = inspectProcessTree({ projectRoot });
|
|
2528
2588
|
let verdict = "NO_PIPELINE_RUNNING";
|
|
2529
2589
|
if (run.status === "running") if (processInfo.orchestrator_pid === null && active === 0 && completed > 0) verdict = "NO_PIPELINE_RUNNING";
|
|
2530
2590
|
else if (processInfo.zombies.length > 0) verdict = "STALLED";
|
|
2591
|
+
else if (processInfo.orchestrator_pid !== null && processInfo.child_pids.length > 0 && stalenessSeconds > DEFAULT_STALL_THRESHOLD_SECONDS) verdict = "HEALTHY";
|
|
2531
2592
|
else if (stalenessSeconds > DEFAULT_STALL_THRESHOLD_SECONDS) verdict = "STALLED";
|
|
2532
2593
|
else if (processInfo.orchestrator_pid !== null && processInfo.child_pids.length === 0 && active > 0) verdict = "STALLED";
|
|
2533
2594
|
else verdict = "HEALTHY";
|
|
@@ -2660,6 +2721,7 @@ function defaultSupervisorDeps() {
|
|
|
2660
2721
|
};
|
|
2661
2722
|
}
|
|
2662
2723
|
},
|
|
2724
|
+
getAllDescendants: (rootPids) => getAllDescendantPids(rootPids),
|
|
2663
2725
|
runAnalysis: async (runId, projectRoot) => {
|
|
2664
2726
|
const dbPath = join(projectRoot, ".substrate", "substrate.db");
|
|
2665
2727
|
if (!existsSync(dbPath)) return;
|
|
@@ -2701,7 +2763,7 @@ function defaultSupervisorDeps() {
|
|
|
2701
2763
|
*/
|
|
2702
2764
|
async function runSupervisorAction(options, deps = {}) {
|
|
2703
2765
|
const { pollInterval, stallThreshold, maxRestarts, outputFormat, projectRoot, runId, pack, experiment, maxExperiments } = options;
|
|
2704
|
-
const { getHealth, killPid, resumePipeline, sleep, incrementRestarts, runAnalysis, getTokenSnapshot } = {
|
|
2766
|
+
const { getHealth, killPid, resumePipeline, sleep, incrementRestarts, runAnalysis, getTokenSnapshot, getAllDescendants } = {
|
|
2705
2767
|
...defaultSupervisorDeps(),
|
|
2706
2768
|
...deps
|
|
2707
2769
|
};
|
|
@@ -2833,7 +2895,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
2833
2895
|
const expDb = expDbWrapper.db;
|
|
2834
2896
|
const { runRunAction: runPipeline } = await import(
|
|
2835
2897
|
/* @vite-ignore */
|
|
2836
|
-
"../run-
|
|
2898
|
+
"../run-B9IglY4m.js"
|
|
2837
2899
|
);
|
|
2838
2900
|
const runStoryFn = async (opts) => {
|
|
2839
2901
|
const exitCode = await runPipeline({
|
|
@@ -2899,7 +2961,10 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
2899
2961
|
return failed.length > 0 || escalated.length > 0 ? 1 : 0;
|
|
2900
2962
|
}
|
|
2901
2963
|
if (health.staleness_seconds >= stallThreshold) {
|
|
2902
|
-
const
|
|
2964
|
+
const directPids = [...health.process.orchestrator_pid !== null ? [health.process.orchestrator_pid] : [], ...health.process.child_pids];
|
|
2965
|
+
const descendantPids = getAllDescendants(directPids);
|
|
2966
|
+
const directPidSet = new Set(directPids);
|
|
2967
|
+
const pids = [...directPids, ...descendantPids.filter((p) => !directPidSet.has(p))];
|
|
2903
2968
|
emitEvent({
|
|
2904
2969
|
type: "supervisor:kill",
|
|
2905
2970
|
run_id: health.run_id,
|
|
@@ -2973,6 +3038,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
2973
3038
|
function registerSupervisorCommand(program, _version = "0.0.0", projectRoot = process.cwd()) {
|
|
2974
3039
|
program.command("supervisor").description("Monitor a pipeline run and automatically recover from stalls").option("--poll-interval <seconds>", "Health poll interval in seconds", (v) => parseInt(v, 10), 60).option("--stall-threshold <seconds>", "Staleness in seconds before killing a stalled pipeline", (v) => parseInt(v, 10), 600).option("--max-restarts <n>", "Maximum automatic restarts before aborting", (v) => parseInt(v, 10), 3).option("--run-id <id>", "Pipeline run ID to monitor (defaults to latest)").option("--pack <name>", "Methodology pack name", "bmad").option("--project-root <path>", "Project root directory", projectRoot).option("--output-format <format>", "Output format: human (default) or json", "human").option("--experiment", "After post-run analysis, enter experiment mode: create branches, apply modifications, run single-story experiments, and report verdicts (Story 17-4)", false).option("--max-experiments <n>", "Maximum number of experiments to run per analysis cycle (default: 2, Story 17-4 AC6)", (v) => parseInt(v, 10), 2).action(async (opts) => {
|
|
2975
3040
|
const outputFormat = opts.outputFormat === "json" ? "json" : "human";
|
|
3041
|
+
if (opts.stallThreshold < 120) console.warn(`Warning: --stall-threshold ${opts.stallThreshold}s is below 120s. Agent steps typically take 45-90s. This may cause false stall detections and wasted restarts.`);
|
|
2976
3042
|
const exitCode = await runSupervisorAction({
|
|
2977
3043
|
pollInterval: opts.pollInterval,
|
|
2978
3044
|
stallThreshold: opts.stallThreshold,
|
package/dist/index.d.ts
CHANGED
|
@@ -1025,6 +1025,8 @@ interface OrchestratorEvents {
|
|
|
1025
1025
|
storyKey: string;
|
|
1026
1026
|
phase: string;
|
|
1027
1027
|
elapsedMs: number;
|
|
1028
|
+
/** PID of the stalled child process, or null if not tracked */
|
|
1029
|
+
childPid: number | null;
|
|
1028
1030
|
};
|
|
1029
1031
|
/** Readiness check has completed — emitted for all verdicts (READY, NEEDS_WORK, NOT_READY) */
|
|
1030
1032
|
'solutioning:readiness-check': {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./logger-C6n1g8uP.js";
|
|
2
2
|
import "./event-bus-J-bw-pkp.js";
|
|
3
|
-
import { registerRunCommand, runRunAction } from "./run-
|
|
3
|
+
import { registerRunCommand, runRunAction } from "./run-CoP8UQU3.js";
|
|
4
4
|
import "./decisions-DNYByk0U.js";
|
|
5
5
|
import "./metrics-BSg8VIHd.js";
|
|
6
6
|
|
|
@@ -1202,6 +1202,15 @@ function buildPipelineStatusOutput(run, tokenSummary, decisionsCount, storiesCou
|
|
|
1202
1202
|
totalOutput += row.total_output_tokens;
|
|
1203
1203
|
totalCost += row.total_cost_usd;
|
|
1204
1204
|
}
|
|
1205
|
+
let activeDispatches = 0;
|
|
1206
|
+
try {
|
|
1207
|
+
if (run.token_usage_json) {
|
|
1208
|
+
const state = JSON.parse(run.token_usage_json);
|
|
1209
|
+
if (state.stories) {
|
|
1210
|
+
for (const s of Object.values(state.stories)) if (s.phase !== "PENDING" && s.phase !== "COMPLETE" && s.phase !== "ESCALATED") activeDispatches++;
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
} catch {}
|
|
1205
1214
|
return {
|
|
1206
1215
|
run_id: run.id,
|
|
1207
1216
|
current_phase: currentPhase,
|
|
@@ -1214,7 +1223,9 @@ function buildPipelineStatusOutput(run, tokenSummary, decisionsCount, storiesCou
|
|
|
1214
1223
|
decisions_count: decisionsCount,
|
|
1215
1224
|
stories_count: storiesCount,
|
|
1216
1225
|
last_activity: run.updated_at,
|
|
1217
|
-
staleness_seconds: Math.round((Date.now() - parseDbTimestampAsUtc(run.updated_at).getTime()) / 1e3)
|
|
1226
|
+
staleness_seconds: Math.round((Date.now() - parseDbTimestampAsUtc(run.updated_at).getTime()) / 1e3),
|
|
1227
|
+
last_event_ts: run.updated_at,
|
|
1228
|
+
active_dispatches: activeDispatches
|
|
1218
1229
|
};
|
|
1219
1230
|
}
|
|
1220
1231
|
/**
|
|
@@ -2215,10 +2226,14 @@ Options:
|
|
|
2215
2226
|
- \`--stories <keys>\` — Comma-separated story keys to process (e.g., \`7-1,7-2\`)
|
|
2216
2227
|
- \`--verbose\` — Enable verbose logging output
|
|
2217
2228
|
- \`--pack <name>\` — Methodology pack name (default: bmad)
|
|
2218
|
-
- \`--from <phase>\` — Start from this phase: analysis, planning, solutioning, implementation
|
|
2229
|
+
- \`--from <phase>\` — Start from this phase: research, analysis, planning, solutioning, implementation
|
|
2219
2230
|
- \`--stop-after <phase>\` — Stop pipeline after this phase completes
|
|
2220
2231
|
- \`--concurrency <n>\` — Maximum parallel conflict groups (default: 3)
|
|
2221
2232
|
- \`--output-format <format>\` — Output format: human (default) or json
|
|
2233
|
+
- \`--concept <text>\` — Inline concept text (required when --from analysis)
|
|
2234
|
+
- \`--research\` — Enable the research phase even if not set in the pack config
|
|
2235
|
+
- \`--skip-research\` — Skip the research phase even if enabled in the pack config
|
|
2236
|
+
- \`--skip-ux\` — Skip the UX design phase even if enabled in the pack config
|
|
2222
2237
|
- \`--help-agent\` — Print this agent instruction fragment and exit
|
|
2223
2238
|
|
|
2224
2239
|
Examples:
|
|
@@ -2285,12 +2300,51 @@ Options:
|
|
|
2285
2300
|
- \`--analysis <run-id>\` — Read and output the analysis report with optimization recommendations for a specific run
|
|
2286
2301
|
- \`--output-format <format>\` — Output format: human (default) or json
|
|
2287
2302
|
|
|
2303
|
+
### substrate export
|
|
2304
|
+
Export decision store contents as human-readable markdown files.
|
|
2305
|
+
|
|
2306
|
+
\`\`\`
|
|
2307
|
+
substrate export [options]
|
|
2308
|
+
\`\`\`
|
|
2309
|
+
|
|
2310
|
+
Options:
|
|
2311
|
+
- \`--run-id <id>\` — Pipeline run ID to export (defaults to latest run)
|
|
2312
|
+
- \`--output-dir <path>\` — Directory to write exported files to (default: _bmad-output/planning-artifacts/)
|
|
2313
|
+
- \`--output-format <format>\` — Output format: human (default) or json
|
|
2314
|
+
|
|
2288
2315
|
### substrate health
|
|
2289
2316
|
Check pipeline health, stall detection, and process status.
|
|
2290
2317
|
|
|
2291
2318
|
\`\`\`
|
|
2292
2319
|
substrate health [--output-format json]
|
|
2293
2320
|
\`\`\`
|
|
2321
|
+
|
|
2322
|
+
### substrate cost
|
|
2323
|
+
Show cost breakdown for the current session.
|
|
2324
|
+
|
|
2325
|
+
\`\`\`
|
|
2326
|
+
substrate cost [--output-format json]
|
|
2327
|
+
\`\`\`
|
|
2328
|
+
|
|
2329
|
+
### substrate amend
|
|
2330
|
+
Run an amendment pipeline against a completed run.
|
|
2331
|
+
|
|
2332
|
+
\`\`\`
|
|
2333
|
+
substrate amend [options]
|
|
2334
|
+
\`\`\`
|
|
2335
|
+
|
|
2336
|
+
### substrate brainstorm
|
|
2337
|
+
Interactive multi-persona brainstorm session with Pragmatic Engineer, Product Thinker, and Devil's Advocate.
|
|
2338
|
+
|
|
2339
|
+
\`\`\`
|
|
2340
|
+
substrate brainstorm [options]
|
|
2341
|
+
\`\`\`
|
|
2342
|
+
|
|
2343
|
+
Session commands: \`!wrap\` (save & exit), \`!quit\` (exit without saving), \`!help\`
|
|
2344
|
+
|
|
2345
|
+
## Environment Variables
|
|
2346
|
+
|
|
2347
|
+
- \`SUBSTRATE_MEMORY_THRESHOLD_MB\` — Override the free-memory threshold (in MB) for agent dispatch. Default: 512. On macOS, the conservative memory detection may report low availability even when ample RAM exists. Lower this (e.g., 256) if pipelines stall due to memory pressure false positives.
|
|
2294
2348
|
`;
|
|
2295
2349
|
}
|
|
2296
2350
|
/**
|
|
@@ -2842,7 +2896,14 @@ const logger$12 = createLogger("agent-dispatch");
|
|
|
2842
2896
|
const SHUTDOWN_GRACE_MS = 1e4;
|
|
2843
2897
|
const SHUTDOWN_MAX_WAIT_MS = 3e4;
|
|
2844
2898
|
const CHARS_PER_TOKEN = 4;
|
|
2845
|
-
const MIN_FREE_MEMORY_BYTES =
|
|
2899
|
+
const MIN_FREE_MEMORY_BYTES = (() => {
|
|
2900
|
+
const envMB = process.env.SUBSTRATE_MEMORY_THRESHOLD_MB;
|
|
2901
|
+
if (envMB) {
|
|
2902
|
+
const parsed = parseInt(envMB, 10);
|
|
2903
|
+
if (!isNaN(parsed) && parsed >= 0) return parsed * 1024 * 1024;
|
|
2904
|
+
}
|
|
2905
|
+
return 512 * 1024 * 1024;
|
|
2906
|
+
})();
|
|
2846
2907
|
const MEMORY_PRESSURE_POLL_MS = 1e4;
|
|
2847
2908
|
/**
|
|
2848
2909
|
* Get available system memory in bytes, accounting for platform differences.
|
|
@@ -5309,6 +5370,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5309
5370
|
let _heartbeatTimer = null;
|
|
5310
5371
|
const HEARTBEAT_INTERVAL_MS = 3e4;
|
|
5311
5372
|
const WATCHDOG_TIMEOUT_MS = 6e5;
|
|
5373
|
+
const _stalledStories = new Set();
|
|
5312
5374
|
const _phaseStartMs = new Map();
|
|
5313
5375
|
const _phaseEndMs = new Map();
|
|
5314
5376
|
const _storyDispatches = new Map();
|
|
@@ -5404,6 +5466,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5404
5466
|
}
|
|
5405
5467
|
function recordProgress() {
|
|
5406
5468
|
_lastProgressTs = Date.now();
|
|
5469
|
+
_stalledStories.clear();
|
|
5407
5470
|
}
|
|
5408
5471
|
function startHeartbeat() {
|
|
5409
5472
|
if (_heartbeatTimer !== null) return;
|
|
@@ -5415,7 +5478,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
5415
5478
|
for (const s of _stories.values()) if (s.phase === "COMPLETE" || s.phase === "ESCALATED") completed++;
|
|
5416
5479
|
else if (s.phase === "PENDING") queued++;
|
|
5417
5480
|
else active++;
|
|
5418
|
-
|
|
5481
|
+
const timeSinceProgress = Date.now() - _lastProgressTs;
|
|
5482
|
+
if (timeSinceProgress >= HEARTBEAT_INTERVAL_MS) eventBus.emit("orchestrator:heartbeat", {
|
|
5419
5483
|
runId: config.pipelineRunId ?? "",
|
|
5420
5484
|
activeDispatches: active,
|
|
5421
5485
|
completedDispatches: completed,
|
|
@@ -5424,6 +5488,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
5424
5488
|
const elapsed = Date.now() - _lastProgressTs;
|
|
5425
5489
|
if (elapsed >= WATCHDOG_TIMEOUT_MS) {
|
|
5426
5490
|
for (const [key, s] of _stories) if (s.phase !== "PENDING" && s.phase !== "COMPLETE" && s.phase !== "ESCALATED") {
|
|
5491
|
+
if (_stalledStories.has(key)) continue;
|
|
5492
|
+
_stalledStories.add(key);
|
|
5427
5493
|
logger$16.warn({
|
|
5428
5494
|
storyKey: key,
|
|
5429
5495
|
phase: s.phase,
|
|
@@ -5433,7 +5499,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
5433
5499
|
runId: config.pipelineRunId ?? "",
|
|
5434
5500
|
storyKey: key,
|
|
5435
5501
|
phase: s.phase,
|
|
5436
|
-
elapsedMs: elapsed
|
|
5502
|
+
elapsedMs: elapsed,
|
|
5503
|
+
childPid: null
|
|
5437
5504
|
});
|
|
5438
5505
|
}
|
|
5439
5506
|
}
|
|
@@ -6194,7 +6261,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6194
6261
|
});
|
|
6195
6262
|
persistState();
|
|
6196
6263
|
recordProgress();
|
|
6197
|
-
startHeartbeat();
|
|
6264
|
+
if (config.enableHeartbeat) startHeartbeat();
|
|
6198
6265
|
if (projectRoot !== void 0) {
|
|
6199
6266
|
const seedResult = seedMethodologyContext(db, projectRoot);
|
|
6200
6267
|
if (seedResult.decisionsCreated > 0) logger$16.info({
|
|
@@ -10647,7 +10714,8 @@ async function runRunAction(options) {
|
|
|
10647
10714
|
run_id: payload.runId,
|
|
10648
10715
|
story_key: payload.storyKey,
|
|
10649
10716
|
phase: payload.phase,
|
|
10650
|
-
elapsed_ms: payload.elapsedMs
|
|
10717
|
+
elapsed_ms: payload.elapsedMs,
|
|
10718
|
+
child_pid: payload.childPid
|
|
10651
10719
|
});
|
|
10652
10720
|
});
|
|
10653
10721
|
}
|
|
@@ -10660,7 +10728,8 @@ async function runRunAction(options) {
|
|
|
10660
10728
|
config: {
|
|
10661
10729
|
maxConcurrency: concurrency,
|
|
10662
10730
|
maxReviewCycles: 2,
|
|
10663
|
-
pipelineRunId: pipelineRun.id
|
|
10731
|
+
pipelineRunId: pipelineRun.id,
|
|
10732
|
+
enableHeartbeat: eventsFlag === true
|
|
10664
10733
|
},
|
|
10665
10734
|
projectRoot
|
|
10666
10735
|
});
|
|
@@ -11103,4 +11172,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
|
|
|
11103
11172
|
|
|
11104
11173
|
//#endregion
|
|
11105
11174
|
export { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getSubstrateDefaultSettings, parseDbTimestampAsUtc, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict };
|
|
11106
|
-
//# sourceMappingURL=run-
|
|
11175
|
+
//# sourceMappingURL=run-CoP8UQU3.js.map
|