substrate-ai 0.19.27 → 0.19.29
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/adapter-registry-S_u-oxuM.js +4 -0
- package/dist/cli/index.js +177 -37
- package/dist/{decisions-BYjuW3st.js → decisions-CnvFtZ7P.js} +1 -1
- package/dist/{dist-D0SnP1ns.js → dist-R0W4ofKv.js} +72 -7
- package/dist/{errors-Crc4y34k.js → errors-BJRMJyGb.js} +2 -2
- package/dist/{experimenter-Ajtt8D60.js → experimenter-Sq8sNz9y.js} +1 -1
- package/dist/{health-CAg5B3m_.js → health-BS20i6mY.js} +1225 -23
- package/dist/{health-gww8zfnD.js → health-Cy_9GgQ_.js} +2 -2
- package/dist/index.d.ts +64 -1
- package/dist/index.js +2 -2
- package/dist/{routing-DZuOjyTy.js → routing-CRjtRmIl.js} +1 -1
- package/dist/{run-CsNW777k.js → run-BBYhrXw9.js} +2744 -2056
- package/dist/{run-CH6kOKMs.js → run-D0-aXchh.js} +3 -3
- package/dist/{upgrade-DldTl30b.js → upgrade-DwEbjHWg.js} +2 -2
- package/dist/{upgrade-d21gzvUw.js → upgrade-Q9YROhpA.js} +2 -2
- package/dist/{version-manager-impl-Vlz013ih.js → version-manager-impl-DReRXdW7.js} +1 -1
- package/package.json +1 -1
- package/dist/adapter-registry-Cw2DpQOy.js +0 -4
package/dist/cli/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { FileStateStore, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, WorkGraphRepository, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot } from "../health-
|
|
2
|
+
import { FileStateStore, RunManifest, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveRunManifest } from "../health-BS20i6mY.js";
|
|
3
3
|
import { createLogger } from "../logger-KeHncl-f.js";
|
|
4
4
|
import { createEventBus } from "../helpers-CElYrONe.js";
|
|
5
|
-
import { AdapterRegistry, BudgetConfigSchema, CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, ConfigError, CostTrackerConfigSchema, DEFAULT_CONFIG, DoltClient, DoltNotInstalled, GlobalSettingsSchema, IngestionServer, MonitorDatabaseImpl, OPERATIONAL_FINDING, PartialGlobalSettingsSchema, PartialProviderConfigSchema, ProvidersSchema, RoutingRecommender, STORY_METRICS, TelemetryConfigSchema, addTokenUsage, aggregateTokenUsageForRun, checkDoltInstalled, compareRunMetrics, createAmendmentRun, createConfigSystem, createDecision, createDoltClient, createPipelineRun, getActiveDecisions, getAllCostEntriesFiltered, getBaselineRunMetrics, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestCompletedRun, getLatestRun, getPipelineRunById, getPlanningCostTotal, getRetryableEscalations, getRunMetrics, getRunningPipelineRuns, getSessionCostSummary, getSessionCostSummaryFiltered, getStoryMetricsForRun, getTokenUsageSummary, incrementRunRestarts, initSchema, initializeDolt, listRunMetrics, loadParentRunDecisions, supersedeDecision, tagRunAsBaseline, updatePipelineRun } from "../dist-
|
|
5
|
+
import { AdapterRegistry, BudgetConfigSchema, CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, ConfigError, CostTrackerConfigSchema, DEFAULT_CONFIG, DoltClient, DoltNotInstalled, GlobalSettingsSchema, IngestionServer, MonitorDatabaseImpl, OPERATIONAL_FINDING, PartialGlobalSettingsSchema, PartialProviderConfigSchema, ProvidersSchema, RoutingRecommender, STORY_METRICS, TelemetryConfigSchema, addTokenUsage, aggregateTokenUsageForRun, checkDoltInstalled, compareRunMetrics, createAmendmentRun, createConfigSystem, createDecision, createDoltClient, createPipelineRun, getActiveDecisions, getAllCostEntriesFiltered, getBaselineRunMetrics, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestCompletedRun, getLatestRun, getPipelineRunById, getPlanningCostTotal, getRetryableEscalations, getRunMetrics, getRunningPipelineRuns, getSessionCostSummary, getSessionCostSummaryFiltered, getStoryMetricsForRun, getTokenUsageSummary, incrementRunRestarts, initSchema, initializeDolt, listRunMetrics, loadParentRunDecisions, supersedeDecision, tagRunAsBaseline, updatePipelineRun } from "../dist-R0W4ofKv.js";
|
|
6
6
|
import "../adapter-registry-DXLMTmfD.js";
|
|
7
|
-
import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-
|
|
8
|
-
import "../errors-
|
|
7
|
+
import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-BBYhrXw9.js";
|
|
8
|
+
import "../errors-BJRMJyGb.js";
|
|
9
9
|
import "../routing-CcBOCuC9.js";
|
|
10
10
|
import "../decisions-C0pz9Clx.js";
|
|
11
11
|
import "../version-manager-impl-BmOWu8ml.js";
|
|
12
|
-
import { registerUpgradeCommand } from "../upgrade-
|
|
12
|
+
import { registerUpgradeCommand } from "../upgrade-DwEbjHWg.js";
|
|
13
13
|
import { Command } from "commander";
|
|
14
14
|
import { fileURLToPath } from "url";
|
|
15
15
|
import { dirname, join, resolve } from "path";
|
|
@@ -21,15 +21,16 @@ import * as path$3 from "node:path";
|
|
|
21
21
|
import * as path$2 from "node:path";
|
|
22
22
|
import * as path$1 from "node:path";
|
|
23
23
|
import { join as join$1 } from "node:path";
|
|
24
|
+
import { randomUUID } from "node:crypto";
|
|
24
25
|
import { z } from "zod";
|
|
25
26
|
import * as fs from "node:fs/promises";
|
|
26
27
|
import { access as access$1, readFile as readFile$1, readdir as readdir$1 } from "node:fs/promises";
|
|
27
|
-
import { appendFileSync, chmodSync, cpSync, existsSync as existsSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync, rmSync as rmSync$1, statSync, unlinkSync, writeFileSync as writeFileSync$1 } from "fs";
|
|
28
|
+
import { appendFileSync, chmodSync, cpSync, existsSync as existsSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync, rmSync as rmSync$1, statSync, unlinkSync as unlinkSync$1, writeFileSync as writeFileSync$1 } from "fs";
|
|
28
29
|
import { homedir } from "os";
|
|
29
30
|
import { createRequire } from "node:module";
|
|
30
31
|
import { fileURLToPath as fileURLToPath$1 } from "node:url";
|
|
31
32
|
import { createInterface } from "node:readline";
|
|
32
|
-
import { randomUUID } from "crypto";
|
|
33
|
+
import { randomUUID as randomUUID$1 } from "crypto";
|
|
33
34
|
import { createInterface as createInterface$1 } from "readline";
|
|
34
35
|
|
|
35
36
|
//#region packages/core/dist/git/git-utils.js
|
|
@@ -1801,7 +1802,7 @@ function clearBmadCommandFiles(commandsDir) {
|
|
|
1801
1802
|
try {
|
|
1802
1803
|
const entries = readdirSync$1(commandsDir);
|
|
1803
1804
|
for (const entry of entries) if (entry.startsWith("bmad-") && entry.endsWith(".md")) try {
|
|
1804
|
-
unlinkSync(join(commandsDir, entry));
|
|
1805
|
+
unlinkSync$1(join(commandsDir, entry));
|
|
1805
1806
|
} catch {}
|
|
1806
1807
|
} catch {}
|
|
1807
1808
|
}
|
|
@@ -2761,6 +2762,23 @@ async function runResumeAction(options) {
|
|
|
2761
2762
|
if (Array.isArray(config.explicitStories) && config.explicitStories.length > 0) scopedStories = config.explicitStories;
|
|
2762
2763
|
} catch {}
|
|
2763
2764
|
const dbDir = dbPath.replace("/substrate.db", "");
|
|
2765
|
+
if (options.stories === void 0 || options.stories.length === 0) {
|
|
2766
|
+
const { manifest: resolvedManifest } = await resolveRunManifest(dbRoot, runId);
|
|
2767
|
+
if (resolvedManifest !== null) try {
|
|
2768
|
+
const manifestData = await resolvedManifest.read();
|
|
2769
|
+
const manifestStories = manifestData.cli_flags["stories"] ?? manifestData.story_scope;
|
|
2770
|
+
if (Array.isArray(manifestStories) && manifestStories.length > 0) {
|
|
2771
|
+
scopedStories = manifestStories;
|
|
2772
|
+
logger$13.debug({
|
|
2773
|
+
runId,
|
|
2774
|
+
stories: scopedStories
|
|
2775
|
+
}, "resume scope loaded from manifest");
|
|
2776
|
+
}
|
|
2777
|
+
} catch {
|
|
2778
|
+
logger$13.debug({ runId }, "manifest read failed in resume — using legacy config_json scope");
|
|
2779
|
+
}
|
|
2780
|
+
else logger$13.debug({ runId }, "Run manifest not found for scope preservation — using legacy config_json scope");
|
|
2781
|
+
}
|
|
2764
2782
|
return runFullPipelineFromPhase({
|
|
2765
2783
|
packName,
|
|
2766
2784
|
packPath,
|
|
@@ -3134,6 +3152,55 @@ function registerResumeCommand(program, _version = "0.0.0", projectRoot = proces
|
|
|
3134
3152
|
//#endregion
|
|
3135
3153
|
//#region src/cli/commands/status.ts
|
|
3136
3154
|
const logger$12 = createLogger("status-cmd");
|
|
3155
|
+
/**
|
|
3156
|
+
* Map a manifest per-story status string to the appropriate WorkGraphCounts bucket.
|
|
3157
|
+
* Unknown strings are treated as `inProgress` (safe default).
|
|
3158
|
+
*/
|
|
3159
|
+
function manifestStatusToWorkGraphBucket(status) {
|
|
3160
|
+
switch (status) {
|
|
3161
|
+
case "complete": return "complete";
|
|
3162
|
+
case "escalated": return "escalated";
|
|
3163
|
+
case "failed":
|
|
3164
|
+
case "verification-failed": return "failed";
|
|
3165
|
+
case "dispatched":
|
|
3166
|
+
case "in-review":
|
|
3167
|
+
case "recovered": return "inProgress";
|
|
3168
|
+
case "gated":
|
|
3169
|
+
case "pending": return "ready";
|
|
3170
|
+
default: return "inProgress";
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
/**
|
|
3174
|
+
* Build a WorkGraphSummary from manifest `per_story_state`.
|
|
3175
|
+
* readyStories and blockedStories are left empty — manifest does not carry
|
|
3176
|
+
* dependency-graph detail (only status counts).
|
|
3177
|
+
*/
|
|
3178
|
+
function buildWorkGraphFromManifest(perStoryState) {
|
|
3179
|
+
const counts = {
|
|
3180
|
+
ready: 0,
|
|
3181
|
+
blocked: 0,
|
|
3182
|
+
inProgress: 0,
|
|
3183
|
+
complete: 0,
|
|
3184
|
+
escalated: 0,
|
|
3185
|
+
failed: 0
|
|
3186
|
+
};
|
|
3187
|
+
for (const entry of Object.values(perStoryState)) {
|
|
3188
|
+
const bucket = manifestStatusToWorkGraphBucket(entry.status);
|
|
3189
|
+
counts[bucket]++;
|
|
3190
|
+
}
|
|
3191
|
+
return {
|
|
3192
|
+
summary: {
|
|
3193
|
+
ready: counts.ready,
|
|
3194
|
+
blocked: counts.blocked,
|
|
3195
|
+
inProgress: counts.inProgress,
|
|
3196
|
+
complete: counts.complete,
|
|
3197
|
+
escalated: counts.escalated,
|
|
3198
|
+
failed: counts.failed
|
|
3199
|
+
},
|
|
3200
|
+
readyStories: [],
|
|
3201
|
+
blockedStories: []
|
|
3202
|
+
};
|
|
3203
|
+
}
|
|
3137
3204
|
async function runStatusAction(options) {
|
|
3138
3205
|
const { outputFormat, runId, projectRoot, stateStore, history } = options;
|
|
3139
3206
|
if (history === true) {
|
|
@@ -3176,8 +3243,29 @@ async function runStatusAction(options) {
|
|
|
3176
3243
|
});
|
|
3177
3244
|
try {
|
|
3178
3245
|
await initSchema(adapter);
|
|
3246
|
+
let run;
|
|
3247
|
+
if (runId !== void 0 && runId !== "") run = await getPipelineRunById(adapter, runId);
|
|
3248
|
+
else {
|
|
3249
|
+
let currentRunId;
|
|
3250
|
+
try {
|
|
3251
|
+
const currentRunIdPath = join(dbRoot, ".substrate", "current-run-id");
|
|
3252
|
+
const content = readFileSync$1(currentRunIdPath, "utf-8").trim();
|
|
3253
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3254
|
+
if (UUID_RE.test(content)) currentRunId = content;
|
|
3255
|
+
} catch {}
|
|
3256
|
+
if (currentRunId !== void 0) run = await getPipelineRunById(adapter, currentRunId);
|
|
3257
|
+
if (run === void 0) run = await getLatestRun(adapter);
|
|
3258
|
+
}
|
|
3179
3259
|
let workGraph;
|
|
3180
|
-
|
|
3260
|
+
const { manifest: resolvedManifest } = await resolveRunManifest(dbRoot, run?.id);
|
|
3261
|
+
if (resolvedManifest !== null) try {
|
|
3262
|
+
const manifestData = await resolvedManifest.read();
|
|
3263
|
+
workGraph = buildWorkGraphFromManifest(manifestData.per_story_state);
|
|
3264
|
+
logger$12.debug({ runId: run?.id }, "status: workGraph built from manifest per_story_state");
|
|
3265
|
+
} catch {
|
|
3266
|
+
logger$12.debug({ runId: run?.id }, "status: manifest read failed — falling back to wg_stories");
|
|
3267
|
+
}
|
|
3268
|
+
if (workGraph === void 0) try {
|
|
3181
3269
|
const wgRepo = new WorkGraphRepository(adapter);
|
|
3182
3270
|
const allStories = await adapter.query(`SELECT story_key, title, status FROM wg_stories`);
|
|
3183
3271
|
if (allStories.length > 0) {
|
|
@@ -3214,21 +3302,8 @@ async function runStatusAction(options) {
|
|
|
3214
3302
|
} catch (err) {
|
|
3215
3303
|
logger$12.debug({ err }, "Work graph query failed, continuing without work graph data");
|
|
3216
3304
|
}
|
|
3217
|
-
let run;
|
|
3218
|
-
if (runId !== void 0 && runId !== "") run = await getPipelineRunById(adapter, runId);
|
|
3219
|
-
else {
|
|
3220
|
-
let currentRunId;
|
|
3221
|
-
try {
|
|
3222
|
-
const currentRunIdPath = join(dbRoot, ".substrate", "current-run-id");
|
|
3223
|
-
const content = readFileSync$1(currentRunIdPath, "utf-8").trim();
|
|
3224
|
-
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3225
|
-
if (UUID_RE.test(content)) currentRunId = content;
|
|
3226
|
-
} catch {}
|
|
3227
|
-
if (currentRunId !== void 0) run = await getPipelineRunById(adapter, currentRunId);
|
|
3228
|
-
if (run === void 0) run = await getLatestRun(adapter);
|
|
3229
|
-
}
|
|
3230
3305
|
if (run === void 0) {
|
|
3231
|
-
const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-
|
|
3306
|
+
const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-Cy_9GgQ_.js");
|
|
3232
3307
|
const substrateDirPath = join(projectRoot, ".substrate");
|
|
3233
3308
|
const processInfo = inspectProcessTree$1({
|
|
3234
3309
|
projectRoot,
|
|
@@ -3822,7 +3897,7 @@ async function runAmendAction(options) {
|
|
|
3822
3897
|
}
|
|
3823
3898
|
parentRunId = latestCompleted.id;
|
|
3824
3899
|
}
|
|
3825
|
-
const amendmentRunId = randomUUID();
|
|
3900
|
+
const amendmentRunId = randomUUID$1();
|
|
3826
3901
|
let methodology = packName;
|
|
3827
3902
|
try {
|
|
3828
3903
|
const packLoader$1 = createPackLoader();
|
|
@@ -4044,6 +4119,7 @@ function registerAmendCommand(program, _version = "0.0.0", projectRoot = process
|
|
|
4044
4119
|
|
|
4045
4120
|
//#endregion
|
|
4046
4121
|
//#region src/cli/commands/supervisor.ts
|
|
4122
|
+
const supervisorLogger = createLogger("supervisor-cmd");
|
|
4047
4123
|
function defaultSupervisorDeps() {
|
|
4048
4124
|
return {
|
|
4049
4125
|
getHealth: getAutoHealthData,
|
|
@@ -4115,7 +4191,7 @@ function defaultSupervisorDeps() {
|
|
|
4115
4191
|
if (cached === null) {
|
|
4116
4192
|
const { AdapterRegistry: AR } = await import(
|
|
4117
4193
|
/* @vite-ignore */
|
|
4118
|
-
"../adapter-registry-
|
|
4194
|
+
"../adapter-registry-S_u-oxuM.js"
|
|
4119
4195
|
);
|
|
4120
4196
|
cached = new AR();
|
|
4121
4197
|
await cached.discoverAndRegister();
|
|
@@ -4379,7 +4455,13 @@ async function handleStallRecovery(health, state, config, deps, io) {
|
|
|
4379
4455
|
log(`Supervisor: Restarting pipeline (attempt ${newRestartCount}/${maxRestarts})`);
|
|
4380
4456
|
try {
|
|
4381
4457
|
let scopedStories;
|
|
4382
|
-
if (
|
|
4458
|
+
if (health.run_id !== null) try {
|
|
4459
|
+
const manifest = RunManifest.open(health.run_id, projectRoot);
|
|
4460
|
+
const data = await manifest.read();
|
|
4461
|
+
const manifestStories = data?.cli_flags?.stories;
|
|
4462
|
+
if (Array.isArray(manifestStories) && manifestStories.length > 0) scopedStories = manifestStories;
|
|
4463
|
+
} catch {}
|
|
4464
|
+
if (scopedStories === void 0 && deps.getRunConfig !== void 0 && health.run_id !== null) try {
|
|
4383
4465
|
const runConfig = await deps.getRunConfig(health.run_id, projectRoot);
|
|
4384
4466
|
if (runConfig?.explicitStories !== void 0 && runConfig.explicitStories.length > 0) scopedStories = runConfig.explicitStories;
|
|
4385
4467
|
} catch {}
|
|
@@ -4442,7 +4524,7 @@ async function handleStallRecovery(health, state, config, deps, io) {
|
|
|
4442
4524
|
* 2 — max restarts exceeded (safety valve triggered)
|
|
4443
4525
|
*/
|
|
4444
4526
|
async function runSupervisorAction(options, deps = {}) {
|
|
4445
|
-
const { pollInterval, stallThreshold, maxRestarts, outputFormat, projectRoot, runId, pack, experiment, maxExperiments } = options;
|
|
4527
|
+
const { pollInterval, stallThreshold, maxRestarts, outputFormat, projectRoot, runId, pack, experiment, maxExperiments, force } = options;
|
|
4446
4528
|
const resolvedDeps = {
|
|
4447
4529
|
...defaultSupervisorDeps(),
|
|
4448
4530
|
...deps
|
|
@@ -4455,6 +4537,62 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
4455
4537
|
};
|
|
4456
4538
|
let maxRestartsExhausted = false;
|
|
4457
4539
|
const startTime = Date.now();
|
|
4540
|
+
const sessionId = randomUUID();
|
|
4541
|
+
let supervisorLock = null;
|
|
4542
|
+
/** Track whether process exit handlers have been registered for this supervisor. */
|
|
4543
|
+
let exitHandlersRegistered = false;
|
|
4544
|
+
/**
|
|
4545
|
+
* Register process.once exit handlers to release the lock on exit.
|
|
4546
|
+
* Called exactly once, after the first successful lock acquisition.
|
|
4547
|
+
* Using process.once (not process.on) per Story 52-2 spec.
|
|
4548
|
+
*/
|
|
4549
|
+
function registerExitHandlers(lock) {
|
|
4550
|
+
if (exitHandlersRegistered) return;
|
|
4551
|
+
exitHandlersRegistered = true;
|
|
4552
|
+
process.once("exit", () => {
|
|
4553
|
+
lock.release().catch((e) => {
|
|
4554
|
+
supervisorLogger.debug({ error: e }, "lock release on exit failed");
|
|
4555
|
+
});
|
|
4556
|
+
});
|
|
4557
|
+
process.once("SIGTERM", () => {
|
|
4558
|
+
lock.release().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
4559
|
+
});
|
|
4560
|
+
process.once("SIGINT", () => {
|
|
4561
|
+
lock.release().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
4562
|
+
});
|
|
4563
|
+
}
|
|
4564
|
+
/**
|
|
4565
|
+
* Acquire the supervisor lock for a given run ID.
|
|
4566
|
+
* Non-fatal: logs and continues on failure so the supervisor can still
|
|
4567
|
+
* function in degraded mode without blocking the pipeline.
|
|
4568
|
+
*/
|
|
4569
|
+
async function acquireLockForRun(targetRunId) {
|
|
4570
|
+
if (supervisorLock !== null) return;
|
|
4571
|
+
try {
|
|
4572
|
+
const runsDir = join(projectRoot, ".substrate", "runs");
|
|
4573
|
+
const manifest = RunManifest.open(targetRunId, runsDir);
|
|
4574
|
+
const lock = new SupervisorLock(targetRunId, manifest, supervisorLogger);
|
|
4575
|
+
await lock.acquire(process.pid, sessionId, { force: force ?? false });
|
|
4576
|
+
supervisorLock = lock;
|
|
4577
|
+
supervisorLogger.debug({ runId: targetRunId }, "Supervisor lock acquired");
|
|
4578
|
+
registerExitHandlers(lock);
|
|
4579
|
+
} catch (lockErr) {
|
|
4580
|
+
const msg = lockErr instanceof Error ? lockErr.message : String(lockErr);
|
|
4581
|
+
supervisorLogger.warn({
|
|
4582
|
+
runId: targetRunId,
|
|
4583
|
+
error: msg
|
|
4584
|
+
}, "Supervisor lock acquisition failed");
|
|
4585
|
+
if (outputFormat === "json") process.stdout.write(JSON.stringify({
|
|
4586
|
+
type: "supervisor:lock-failed",
|
|
4587
|
+
run_id: targetRunId,
|
|
4588
|
+
reason: msg,
|
|
4589
|
+
ts: new Date().toISOString()
|
|
4590
|
+
}) + "\n");
|
|
4591
|
+
else process.stderr.write(`Warning: Supervisor lock acquisition failed: ${msg}\n`);
|
|
4592
|
+
if (msg.includes("is already supervised by PID") && !force) throw lockErr;
|
|
4593
|
+
}
|
|
4594
|
+
}
|
|
4595
|
+
if (runId !== void 0) await acquireLockForRun(runId);
|
|
4458
4596
|
function emitEvent(event) {
|
|
4459
4597
|
if (outputFormat === "json") {
|
|
4460
4598
|
const stamped = {
|
|
@@ -4479,6 +4617,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
4479
4617
|
runId: health.run_id
|
|
4480
4618
|
};
|
|
4481
4619
|
log(`Supervisor: auto-bound to active run ${health.run_id}`);
|
|
4620
|
+
await acquireLockForRun(health.run_id);
|
|
4482
4621
|
}
|
|
4483
4622
|
if (outputFormat === "json") {
|
|
4484
4623
|
const tokenSnapshot = health.run_id !== null ? await getTokenSnapshot(health.run_id, projectRoot) : {
|
|
@@ -4557,11 +4696,11 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
4557
4696
|
try {
|
|
4558
4697
|
const { createExperimenter } = await import(
|
|
4559
4698
|
/* @vite-ignore */
|
|
4560
|
-
"../experimenter-
|
|
4699
|
+
"../experimenter-Sq8sNz9y.js"
|
|
4561
4700
|
);
|
|
4562
4701
|
const { getLatestRun: getLatest } = await import(
|
|
4563
4702
|
/* @vite-ignore */
|
|
4564
|
-
"../decisions-
|
|
4703
|
+
"../decisions-CnvFtZ7P.js"
|
|
4565
4704
|
);
|
|
4566
4705
|
const expAdapter = createDatabaseAdapter({
|
|
4567
4706
|
backend: "auto",
|
|
@@ -4571,7 +4710,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
4571
4710
|
await initSchema(expAdapter);
|
|
4572
4711
|
const { runRunAction: runPipeline } = await import(
|
|
4573
4712
|
/* @vite-ignore */
|
|
4574
|
-
"../run-
|
|
4713
|
+
"../run-D0-aXchh.js"
|
|
4575
4714
|
);
|
|
4576
4715
|
const runStoryFn = async (opts) => {
|
|
4577
4716
|
const exitCode = await runPipeline({
|
|
@@ -4780,7 +4919,7 @@ async function runMultiProjectSupervisor(options, deps = {}) {
|
|
|
4780
4919
|
}
|
|
4781
4920
|
}
|
|
4782
4921
|
function registerSupervisorCommand(program, _version = "0.0.0", projectRoot = process.cwd()) {
|
|
4783
|
-
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("--projects <paths>", "Comma-separated project root directories to monitor (multi-project mode)").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) => {
|
|
4922
|
+
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("--projects <paths>", "Comma-separated project root directories to monitor (multi-project mode)").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).option("--force", "Forcefully evict an existing supervisor process (SIGTERM + 500ms) before attaching (Story 52-2)", false).action(async (opts) => {
|
|
4784
4923
|
const outputFormat = opts.outputFormat === "json" ? "json" : "human";
|
|
4785
4924
|
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.`);
|
|
4786
4925
|
if (opts.projects) {
|
|
@@ -4811,7 +4950,8 @@ function registerSupervisorCommand(program, _version = "0.0.0", projectRoot = pr
|
|
|
4811
4950
|
outputFormat,
|
|
4812
4951
|
projectRoot: opts.projectRoot,
|
|
4813
4952
|
experiment: opts.experiment,
|
|
4814
|
-
maxExperiments: opts.maxExperiments
|
|
4953
|
+
maxExperiments: opts.maxExperiments,
|
|
4954
|
+
force: opts.force
|
|
4815
4955
|
});
|
|
4816
4956
|
process.exitCode = exitCode;
|
|
4817
4957
|
});
|
|
@@ -5101,7 +5241,7 @@ async function runMetricsAction(options) {
|
|
|
5101
5241
|
const routingConfigPath = join(dbDir, "routing.yml");
|
|
5102
5242
|
let routingConfig = null;
|
|
5103
5243
|
if (existsSync$1(routingConfigPath)) try {
|
|
5104
|
-
const { loadModelRoutingConfig } = await import("../routing-
|
|
5244
|
+
const { loadModelRoutingConfig } = await import("../routing-CRjtRmIl.js");
|
|
5105
5245
|
routingConfig = loadModelRoutingConfig(routingConfigPath);
|
|
5106
5246
|
} catch {}
|
|
5107
5247
|
if (routingConfig === null) routingConfig = {
|
|
@@ -7461,7 +7601,7 @@ async function runCancelAction(options) {
|
|
|
7461
7601
|
}, "json", true) + "\n");
|
|
7462
7602
|
else process.stdout.write("No running pipeline found.\n");
|
|
7463
7603
|
if (existsSync$1(pidFilePath)) try {
|
|
7464
|
-
unlinkSync(pidFilePath);
|
|
7604
|
+
unlinkSync$1(pidFilePath);
|
|
7465
7605
|
if (outputFormat === "human") process.stdout.write("Cleaned up stale PID file.\n");
|
|
7466
7606
|
} catch {}
|
|
7467
7607
|
return 0;
|
|
@@ -7492,7 +7632,7 @@ async function runCancelAction(options) {
|
|
|
7492
7632
|
killed.push(zombiePid);
|
|
7493
7633
|
} catch {}
|
|
7494
7634
|
if (existsSync$1(pidFilePath)) try {
|
|
7495
|
-
unlinkSync(pidFilePath);
|
|
7635
|
+
unlinkSync$1(pidFilePath);
|
|
7496
7636
|
} catch {}
|
|
7497
7637
|
try {
|
|
7498
7638
|
const adapter = createDatabaseAdapter({
|
|
@@ -8355,8 +8495,8 @@ async function createProgram() {
|
|
|
8355
8495
|
/** Fire-and-forget startup version check (story 8.3, AC3/AC5) */
|
|
8356
8496
|
function checkForUpdatesInBackground(currentVersion) {
|
|
8357
8497
|
if (process.env.SUBSTRATE_NO_UPDATE_CHECK === "1") return;
|
|
8358
|
-
import("../upgrade-
|
|
8359
|
-
const { createVersionManager } = await import("../version-manager-impl-
|
|
8498
|
+
import("../upgrade-Q9YROhpA.js").then(async () => {
|
|
8499
|
+
const { createVersionManager } = await import("../version-manager-impl-DReRXdW7.js");
|
|
8360
8500
|
const vm = createVersionManager();
|
|
8361
8501
|
const result = await vm.checkForUpdates();
|
|
8362
8502
|
if (result.updateAvailable) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, listRequirements, registerArtifact, updateDecision, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./dist-
|
|
1
|
+
import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, listRequirements, registerArtifact, updateDecision, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./dist-R0W4ofKv.js";
|
|
2
2
|
import "./decisions-C0pz9Clx.js";
|
|
3
3
|
|
|
4
4
|
export { getLatestRun };
|
|
@@ -38,7 +38,7 @@ const DEFAULT_TIMEOUTS = {
|
|
|
38
38
|
"create-story": 6e5,
|
|
39
39
|
"dev-story": 18e5,
|
|
40
40
|
"code-review": 9e5,
|
|
41
|
-
"minor-fixes":
|
|
41
|
+
"minor-fixes": 3e5,
|
|
42
42
|
"major-rework": 9e5,
|
|
43
43
|
"readiness-check": 6e5,
|
|
44
44
|
"elicitation": 9e5,
|
|
@@ -213,6 +213,56 @@ function containsAnchorKey(content) {
|
|
|
213
213
|
return YAML_ANCHOR_KEYS.some((key) => content.includes(key));
|
|
214
214
|
}
|
|
215
215
|
/**
|
|
216
|
+
* Merge duplicate top-level YAML keys by concatenating their array values.
|
|
217
|
+
*
|
|
218
|
+
* LLMs sometimes emit the same mapping key twice, splitting a list across two
|
|
219
|
+
* blocks (e.g., `non_functional_requirements:` appears twice, each with
|
|
220
|
+
* different items). This violates YAML 1.2 but is recoverable: we detect
|
|
221
|
+
* duplicate top-level keys and merge their children into a single key.
|
|
222
|
+
*
|
|
223
|
+
* Only operates on top-level keys (no indentation). Nested duplicates are not
|
|
224
|
+
* handled — they're rare in practice and harder to fix safely.
|
|
225
|
+
*/
|
|
226
|
+
function mergeDuplicateYamlKeys(yamlText) {
|
|
227
|
+
const lines = yamlText.split("\n");
|
|
228
|
+
const sections = [];
|
|
229
|
+
let current = null;
|
|
230
|
+
for (const line of lines) {
|
|
231
|
+
const keyMatch = line.match(/^([a-zA-Z_][a-zA-Z0-9_]*):\s*(.*)$/);
|
|
232
|
+
if (keyMatch) {
|
|
233
|
+
const key = keyMatch[1];
|
|
234
|
+
const rest = keyMatch[2].trim();
|
|
235
|
+
current = {
|
|
236
|
+
key,
|
|
237
|
+
lines: [line]
|
|
238
|
+
};
|
|
239
|
+
sections.push(current);
|
|
240
|
+
if (rest.length > 0 && !rest.startsWith("#")) current = null;
|
|
241
|
+
} else if (current) current.lines.push(line);
|
|
242
|
+
else sections.push({
|
|
243
|
+
key: "",
|
|
244
|
+
lines: [line]
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
const seen = new Map();
|
|
248
|
+
const merged = [];
|
|
249
|
+
for (const section of sections) {
|
|
250
|
+
if (section.key === "") {
|
|
251
|
+
merged.push(section);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const existing = seen.get(section.key);
|
|
255
|
+
if (existing !== void 0) {
|
|
256
|
+
const target = merged[existing];
|
|
257
|
+
target.lines.push(...section.lines.slice(1));
|
|
258
|
+
} else {
|
|
259
|
+
seen.set(section.key, merged.length);
|
|
260
|
+
merged.push(section);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return merged.flatMap((s) => s.lines).join("\n");
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
216
266
|
* Parse a YAML string and optionally validate it against a Zod schema.
|
|
217
267
|
*
|
|
218
268
|
* @param yamlText - Raw YAML string to parse
|
|
@@ -225,7 +275,15 @@ function parseYamlResult(yamlText, schema) {
|
|
|
225
275
|
raw = yaml.load(sanitizeYamlEscapes(yamlText));
|
|
226
276
|
} catch (err) {
|
|
227
277
|
const message = err instanceof Error ? err.message : String(err);
|
|
228
|
-
|
|
278
|
+
if (message.includes("duplicated mapping key")) try {
|
|
279
|
+
raw = yaml.load(sanitizeYamlEscapes(mergeDuplicateYamlKeys(yamlText)));
|
|
280
|
+
} catch {
|
|
281
|
+
return {
|
|
282
|
+
parsed: null,
|
|
283
|
+
error: `YAML parse error: ${message}`
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
else return {
|
|
229
287
|
parsed: null,
|
|
230
288
|
error: `YAML parse error: ${message}`
|
|
231
289
|
};
|
|
@@ -4098,7 +4156,10 @@ var DoltQueryError = class extends Error {
|
|
|
4098
4156
|
*/
|
|
4099
4157
|
function runExecFile(cmd, args, opts) {
|
|
4100
4158
|
return new Promise((resolve$2, reject) => {
|
|
4101
|
-
execFile(cmd, args,
|
|
4159
|
+
execFile(cmd, args, {
|
|
4160
|
+
...opts,
|
|
4161
|
+
maxBuffer: 10 * 1024 * 1024
|
|
4162
|
+
}, (err, stdout, stderr) => {
|
|
4102
4163
|
if (err) reject(err);
|
|
4103
4164
|
else resolve$2({
|
|
4104
4165
|
stdout,
|
|
@@ -5383,7 +5444,9 @@ const BudgetConfigSchema = z.object({
|
|
|
5383
5444
|
}).strict();
|
|
5384
5445
|
const TelemetryConfigSchema = z.object({
|
|
5385
5446
|
enabled: z.boolean().default(false),
|
|
5386
|
-
port: z.number().int().min(1).max(65535).default(4318)
|
|
5447
|
+
port: z.number().int().min(1).max(65535).default(4318),
|
|
5448
|
+
meshUrl: z.string().url().optional(),
|
|
5449
|
+
projectId: z.string().optional()
|
|
5387
5450
|
}).strict();
|
|
5388
5451
|
/** Current supported config format version */
|
|
5389
5452
|
const CURRENT_CONFIG_FORMAT_VERSION = "1";
|
|
@@ -5400,7 +5463,8 @@ const SubstrateConfigSchema = z.object({
|
|
|
5400
5463
|
providers: ProvidersSchema,
|
|
5401
5464
|
cost_tracker: CostTrackerConfigSchema.optional(),
|
|
5402
5465
|
budget: BudgetConfigSchema.optional(),
|
|
5403
|
-
telemetry: TelemetryConfigSchema.optional()
|
|
5466
|
+
telemetry: TelemetryConfigSchema.optional(),
|
|
5467
|
+
trivialOutputThreshold: z.number().int().nonnegative().optional()
|
|
5404
5468
|
}).passthrough();
|
|
5405
5469
|
const PartialProviderConfigSchema = ProviderConfigSchema.partial();
|
|
5406
5470
|
const PartialGlobalSettingsSchema = GlobalSettingsSchema.partial();
|
|
@@ -5415,7 +5479,8 @@ const PartialSubstrateConfigSchema = z.object({
|
|
|
5415
5479
|
}).partial().optional(),
|
|
5416
5480
|
cost_tracker: CostTrackerConfigSchema.partial().optional(),
|
|
5417
5481
|
budget: BudgetConfigSchema.partial().optional(),
|
|
5418
|
-
telemetry: TelemetryConfigSchema.partial().optional()
|
|
5482
|
+
telemetry: TelemetryConfigSchema.partial().optional(),
|
|
5483
|
+
trivialOutputThreshold: z.number().int().nonnegative().optional()
|
|
5419
5484
|
}).passthrough();
|
|
5420
5485
|
|
|
5421
5486
|
//#endregion
|
|
@@ -10453,4 +10518,4 @@ async function callLLM(params) {
|
|
|
10453
10518
|
|
|
10454
10519
|
//#endregion
|
|
10455
10520
|
export { ADVISORY_NOTES, AdapterRegistry, AdtError, BudgetConfigSchema, CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, Categorizer, ClaudeCodeAdapter, CodexCLIAdapter, ConfigError, ConfigIncompatibleFormatError, ConsumerAnalyzer, CostTrackerConfigSchema, DEFAULT_CONFIG, DEFAULT_GLOBAL_SETTINGS, DispatcherImpl, DoltClient, DoltNotInstalled, DoltQueryError, ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, EfficiencyScorer, GeminiCLIAdapter, GlobalSettingsSchema, IngestionServer, LogTurnAnalyzer, ModelRoutingConfigSchema, MonitorDatabaseImpl, OPERATIONAL_FINDING, PartialGlobalSettingsSchema, PartialProviderConfigSchema, ProviderPolicySchema, ProvidersSchema, Recommender, RoutingConfigError, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, STORY_METRICS, STORY_OUTCOME, SubstrateConfigSchema, TASK_TYPE_PHASE_MAP, TEST_EXPANSION_FINDING, TEST_PLAN, TelemetryConfigSchema, TelemetryNormalizer, TelemetryPipeline, TurnAnalyzer, VersionManagerImpl, addTokenUsage, aggregateTokenUsageForRun, aggregateTokenUsageForStory, buildAuditLogEntry, buildBranchName, buildModificationDirective, buildPRBody, buildWorktreePath, callLLM, checkDoltInstalled, compareRunMetrics, createAmendmentRun, createConfigSystem, createDatabaseAdapter as createDatabaseAdapter$1, createDecision, createDoltClient, createExperimenter, createPipelineRun, createRequirement, createVersionManager, detectInterfaceChanges, determineVerdict, getActiveDecisions, getAllCostEntriesFiltered, getArtifactByTypeForRun, getArtifactsByRun, getBaselineRunMetrics, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestCompletedRun, getLatestRun, getModelTier, getPipelineRunById, getPlanningCostTotal, getRetryableEscalations, getRunMetrics, getRunningPipelineRuns, getSessionCostSummary, getSessionCostSummaryFiltered, getStoryMetricsForRun, getTokenUsageSummary, incrementRunRestarts, initSchema, initializeDolt, listRequirements, listRunMetrics, loadModelRoutingConfig, loadParentRunDecisions, registerArtifact, resolvePromptFile, supersedeDecision, tagRunAsBaseline, updateDecision, updatePipelineRun, updatePipelineRunConfig, upsertDecision, writeRunMetrics, writeStoryMetrics };
|
|
10456
|
-
//# sourceMappingURL=dist-
|
|
10521
|
+
//# sourceMappingURL=dist-R0W4ofKv.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AdtError } from "./dist-
|
|
1
|
+
import { AdtError } from "./dist-R0W4ofKv.js";
|
|
2
2
|
|
|
3
3
|
//#region src/core/errors.ts
|
|
4
4
|
/** Error thrown when task configuration is invalid */
|
|
@@ -71,4 +71,4 @@ var TaskGraphIncompatibleFormatError = class extends AdtError {
|
|
|
71
71
|
|
|
72
72
|
//#endregion
|
|
73
73
|
export { BudgetExceededError, GitError, RecoveryError, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, WorkerError, WorkerNotFoundError };
|
|
74
|
-
//# sourceMappingURL=errors-
|
|
74
|
+
//# sourceMappingURL=errors-BJRMJyGb.js.map
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { buildAuditLogEntry, buildBranchName, buildModificationDirective, buildPRBody, buildWorktreePath, createExperimenter, determineVerdict, resolvePromptFile } from "./dist-
|
|
1
|
+
import { buildAuditLogEntry, buildBranchName, buildModificationDirective, buildPRBody, buildWorktreePath, createExperimenter, determineVerdict, resolvePromptFile } from "./dist-R0W4ofKv.js";
|
|
2
2
|
|
|
3
3
|
export { createExperimenter };
|