@nathapp/nax 0.68.0 → 0.68.2
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/nax.js +443 -142
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -17735,19 +17735,30 @@ function formatStoryStart(entry, c, _timestamp, mode) {
|
|
|
17735
17735
|
const complexity = typeof data.complexity === "string" ? data.complexity : "unknown";
|
|
17736
17736
|
const tier = typeof data.modelTier === "string" ? data.modelTier : "unknown";
|
|
17737
17737
|
const attempt = typeof data.attempt === "number" ? data.attempt : 1;
|
|
17738
|
+
const agent = typeof data.agent === "string" ? data.agent : undefined;
|
|
17739
|
+
const progress = typeof data.storyNumber === "number" && typeof data.storyTotal === "number" ? `${data.storyNumber}/${data.storyTotal}` : undefined;
|
|
17738
17740
|
const lines = [];
|
|
17739
17741
|
lines.push("");
|
|
17740
17742
|
lines.push(c.bold(`${EMOJI.storyStart} ${c.cyan(storyId)}: ${title}`));
|
|
17741
17743
|
if (mode === "verbose") {
|
|
17744
|
+
if (progress)
|
|
17745
|
+
lines.push(` ${c.gray("\u251C\u2500")} Story: ${c.cyan(progress)}`);
|
|
17742
17746
|
lines.push(` ${c.gray("\u251C\u2500")} Complexity: ${c.yellow(complexity)}`);
|
|
17743
17747
|
lines.push(` ${c.gray("\u251C\u2500")} Tier: ${c.magenta(tier)}`);
|
|
17748
|
+
if (agent)
|
|
17749
|
+
lines.push(` ${c.gray("\u251C\u2500")} Agent: ${c.cyan(agent)}`);
|
|
17744
17750
|
if (attempt > 1) {
|
|
17745
17751
|
lines.push(` ${c.gray("\u2514\u2500")} Attempt: ${c.yellow(`#${attempt}`)} ${EMOJI.retry}`);
|
|
17746
17752
|
} else {
|
|
17747
17753
|
lines.push(` ${c.gray("\u2514\u2500")} Status: ${c.green("starting")}`);
|
|
17748
17754
|
}
|
|
17749
17755
|
} else {
|
|
17750
|
-
const metadata = [
|
|
17756
|
+
const metadata = [];
|
|
17757
|
+
if (progress)
|
|
17758
|
+
metadata.push(progress);
|
|
17759
|
+
metadata.push(complexity, tier);
|
|
17760
|
+
if (agent)
|
|
17761
|
+
metadata.push(agent);
|
|
17751
17762
|
if (attempt > 1)
|
|
17752
17763
|
metadata.push(`attempt #${attempt} ${EMOJI.retry}`);
|
|
17753
17764
|
lines.push(` ${c.gray(metadata.join(" \u2022 "))}`);
|
|
@@ -17809,24 +17820,19 @@ function formatDefault(entry, c, timestamp, mode) {
|
|
|
17809
17820
|
if (entry.storyId) {
|
|
17810
17821
|
parts.push(c.dim(`[${entry.storyId}]`));
|
|
17811
17822
|
}
|
|
17823
|
+
if (entry.sessionRole) {
|
|
17824
|
+
parts.push(c.dim(`(${entry.sessionRole})`));
|
|
17825
|
+
}
|
|
17812
17826
|
parts.push(entry.message);
|
|
17813
17827
|
let output = parts.join(" ");
|
|
17814
17828
|
const data = entry.data;
|
|
17815
17829
|
if (data && typeof data === "object") {
|
|
17816
|
-
const meta3 =
|
|
17817
|
-
if (typeof data.cost === "number" && data.cost > 0)
|
|
17818
|
-
meta3.push(`${EMOJI.cost} ${formatCost(data.cost)}`);
|
|
17819
|
-
if (typeof data.durationMs === "number" && data.durationMs > 0)
|
|
17820
|
-
meta3.push(`${EMOJI.duration} ${formatDuration(data.durationMs)}`);
|
|
17821
|
-
if (typeof data.action === "string")
|
|
17822
|
-
meta3.push(`action: ${data.action}`);
|
|
17823
|
-
if (typeof data.reason === "string" && mode !== "quiet")
|
|
17824
|
-
meta3.push(data.reason);
|
|
17830
|
+
const meta3 = buildDefaultMeta(data, mode);
|
|
17825
17831
|
if (meta3.length > 0) {
|
|
17826
17832
|
output += ` ${c.gray(meta3.join(" "))}`;
|
|
17827
17833
|
}
|
|
17828
17834
|
if (mode === "verbose") {
|
|
17829
|
-
const
|
|
17835
|
+
const filtered = stripConsumedMetaFields(data);
|
|
17830
17836
|
if (Object.keys(filtered).length > 0) {
|
|
17831
17837
|
output += `
|
|
17832
17838
|
${c.gray(JSON.stringify(filtered, null, 2))}`;
|
|
@@ -17838,6 +17844,52 @@ ${c.gray(JSON.stringify(filtered, null, 2))}`;
|
|
|
17838
17844
|
shouldDisplay: true
|
|
17839
17845
|
};
|
|
17840
17846
|
}
|
|
17847
|
+
function buildDefaultMeta(data, mode) {
|
|
17848
|
+
const meta3 = [];
|
|
17849
|
+
const identity = [data.agentName, data.model].filter((v) => typeof v === "string" && v.length > 0);
|
|
17850
|
+
if (identity.length > 0)
|
|
17851
|
+
meta3.push(`${EMOJI.agent} ${identity.join("\xB7")}`);
|
|
17852
|
+
if (typeof data.phaseIndex === "number" && typeof data.totalPhases === "number") {
|
|
17853
|
+
meta3.push(`${data.phaseIndex}/${data.totalPhases}`);
|
|
17854
|
+
}
|
|
17855
|
+
if (typeof data.status === "string")
|
|
17856
|
+
meta3.push(`status: ${data.status}`);
|
|
17857
|
+
if (typeof data.findingsCount === "number")
|
|
17858
|
+
meta3.push(`${data.findingsCount} finding${data.findingsCount === 1 ? "" : "s"}`);
|
|
17859
|
+
const activity = buildActivityMeta(data);
|
|
17860
|
+
if (activity)
|
|
17861
|
+
meta3.push(activity);
|
|
17862
|
+
if (typeof data.cost === "number" && data.cost > 0)
|
|
17863
|
+
meta3.push(`${EMOJI.cost} ${formatCost(data.cost)}`);
|
|
17864
|
+
if (typeof data.durationMs === "number" && data.durationMs > 0)
|
|
17865
|
+
meta3.push(`${EMOJI.duration} ${formatDuration(data.durationMs)}`);
|
|
17866
|
+
if (typeof data.action === "string")
|
|
17867
|
+
meta3.push(`action: ${data.action}`);
|
|
17868
|
+
if (typeof data.reason === "string" && mode !== "quiet")
|
|
17869
|
+
meta3.push(data.reason);
|
|
17870
|
+
return meta3;
|
|
17871
|
+
}
|
|
17872
|
+
function buildActivityMeta(data) {
|
|
17873
|
+
const segments = [];
|
|
17874
|
+
if (typeof data.messageUpdates === "number" && data.messageUpdates > 0)
|
|
17875
|
+
segments.push(`msg ${data.messageUpdates}`);
|
|
17876
|
+
if (typeof data.toolCallUpdates === "number" && data.toolCallUpdates > 0)
|
|
17877
|
+
segments.push(`tools ${data.toolCallUpdates}`);
|
|
17878
|
+
if (typeof data.thinkingUpdates === "number" && data.thinkingUpdates > 0)
|
|
17879
|
+
segments.push(`think ${data.thinkingUpdates}`);
|
|
17880
|
+
if (typeof data.idleMs === "number" && data.idleMs > 0)
|
|
17881
|
+
segments.push(`idle ${formatDuration(data.idleMs)}`);
|
|
17882
|
+
return segments.length > 0 ? segments.join(" ") : null;
|
|
17883
|
+
}
|
|
17884
|
+
function stripConsumedMetaFields(data) {
|
|
17885
|
+
const filtered = {};
|
|
17886
|
+
for (const [key, value] of Object.entries(data)) {
|
|
17887
|
+
if (!CONSUMED_META_KEYS.includes(key)) {
|
|
17888
|
+
filtered[key] = value;
|
|
17889
|
+
}
|
|
17890
|
+
}
|
|
17891
|
+
return filtered;
|
|
17892
|
+
}
|
|
17841
17893
|
function formatRunSummary(summary, options) {
|
|
17842
17894
|
const { mode, useColor = true } = options;
|
|
17843
17895
|
if (mode === "json") {
|
|
@@ -17882,9 +17934,26 @@ function createNoopChalk() {
|
|
|
17882
17934
|
cyan: noop
|
|
17883
17935
|
};
|
|
17884
17936
|
}
|
|
17937
|
+
var CONSUMED_META_KEYS;
|
|
17885
17938
|
var init_formatter = __esm(() => {
|
|
17886
17939
|
init_source();
|
|
17887
17940
|
init_types2();
|
|
17941
|
+
CONSUMED_META_KEYS = [
|
|
17942
|
+
"agentName",
|
|
17943
|
+
"model",
|
|
17944
|
+
"phaseIndex",
|
|
17945
|
+
"totalPhases",
|
|
17946
|
+
"status",
|
|
17947
|
+
"findingsCount",
|
|
17948
|
+
"messageUpdates",
|
|
17949
|
+
"toolCallUpdates",
|
|
17950
|
+
"thinkingUpdates",
|
|
17951
|
+
"idleMs",
|
|
17952
|
+
"cost",
|
|
17953
|
+
"durationMs",
|
|
17954
|
+
"action",
|
|
17955
|
+
"reason"
|
|
17956
|
+
];
|
|
17888
17957
|
});
|
|
17889
17958
|
|
|
17890
17959
|
// src/logging/index.ts
|
|
@@ -17933,6 +18002,43 @@ var init_formatters = __esm(() => {
|
|
|
17933
18002
|
init_source();
|
|
17934
18003
|
});
|
|
17935
18004
|
|
|
18005
|
+
// src/logger/redact.ts
|
|
18006
|
+
function redactString(value) {
|
|
18007
|
+
let out = value;
|
|
18008
|
+
for (const re of SECRET_VALUE_PATTERNS) {
|
|
18009
|
+
re.lastIndex = 0;
|
|
18010
|
+
out = out.replace(re, REDACTED);
|
|
18011
|
+
}
|
|
18012
|
+
return out;
|
|
18013
|
+
}
|
|
18014
|
+
function redactSecrets(input) {
|
|
18015
|
+
if (typeof input === "string")
|
|
18016
|
+
return redactString(input);
|
|
18017
|
+
if (Array.isArray(input))
|
|
18018
|
+
return input.map(redactSecrets);
|
|
18019
|
+
if (input !== null && typeof input === "object") {
|
|
18020
|
+
const out = {};
|
|
18021
|
+
for (const [key, value] of Object.entries(input)) {
|
|
18022
|
+
out[key] = SECRET_KEY_PATTERN.test(key) ? REDACTED : redactSecrets(value);
|
|
18023
|
+
}
|
|
18024
|
+
return out;
|
|
18025
|
+
}
|
|
18026
|
+
return input;
|
|
18027
|
+
}
|
|
18028
|
+
var SECRET_KEY_PATTERN, SECRET_VALUE_PATTERNS, REDACTED = "[REDACTED]";
|
|
18029
|
+
var init_redact = __esm(() => {
|
|
18030
|
+
SECRET_KEY_PATTERN = /(SECRET|TOKEN|API_?KEY|PASSWORD|PRIVATE_?KEY|ACCESS_?KEY|WEBHOOK)/i;
|
|
18031
|
+
SECRET_VALUE_PATTERNS = [
|
|
18032
|
+
/sk-[A-Za-z0-9_-]{16,}/g,
|
|
18033
|
+
/ghp_[A-Za-z0-9]{16,}/g,
|
|
18034
|
+
/gh[opsu]_[A-Za-z0-9]{16,}/g,
|
|
18035
|
+
/npm_[A-Za-z0-9]{8,}/g,
|
|
18036
|
+
/AKIA[0-9A-Z]{16}/g,
|
|
18037
|
+
/xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
18038
|
+
/(?:SECRET|TOKEN|API_?KEY|PASSWORD|PRIVATE_?KEY|ACCESS_?KEY|WEBHOOK)=[^\s"',]+/gi
|
|
18039
|
+
];
|
|
18040
|
+
});
|
|
18041
|
+
|
|
17936
18042
|
// src/logger/logger.ts
|
|
17937
18043
|
import { mkdirSync } from "fs";
|
|
17938
18044
|
import { appendFile } from "fs/promises";
|
|
@@ -18027,7 +18133,8 @@ ${JSON.stringify(entry.data, null, 2)}`;
|
|
|
18027
18133
|
writeToFile(entry) {
|
|
18028
18134
|
if (!this.filePath)
|
|
18029
18135
|
return;
|
|
18030
|
-
const
|
|
18136
|
+
const safeEntry = entry.data ? { ...entry, data: redactSecrets(entry.data) } : entry;
|
|
18137
|
+
const line = `${formatJsonl(safeEntry)}
|
|
18031
18138
|
`;
|
|
18032
18139
|
const filePath = this.filePath;
|
|
18033
18140
|
this.writeQueueTail = this.writeQueueTail.then(() => appendFile(filePath, line).catch((error48) => {
|
|
@@ -18089,6 +18196,7 @@ var LOG_LEVEL_PRIORITY, instance = null, noopLogger;
|
|
|
18089
18196
|
var init_logger = __esm(() => {
|
|
18090
18197
|
init_logging();
|
|
18091
18198
|
init_formatters();
|
|
18199
|
+
init_redact();
|
|
18092
18200
|
LOG_LEVEL_PRIORITY = {
|
|
18093
18201
|
silent: -1,
|
|
18094
18202
|
error: 0,
|
|
@@ -21971,6 +22079,10 @@ function tryParseLLMJson(text) {
|
|
|
21971
22079
|
}
|
|
21972
22080
|
|
|
21973
22081
|
// src/agents/retry/parse-retry.ts
|
|
22082
|
+
function previewOutput(output, maxBytes) {
|
|
22083
|
+
const collapsed = output.replace(/\s+/g, " ").trim();
|
|
22084
|
+
return collapsed.length > maxBytes ? `${collapsed.slice(0, maxBytes)}\u2026` : collapsed;
|
|
22085
|
+
}
|
|
21974
22086
|
function makeParseRetryStrategy(opts) {
|
|
21975
22087
|
const parse5 = opts.parse ?? tryParseLLMJson;
|
|
21976
22088
|
const checkTruncated = opts.looksTruncated ?? looksLikeTruncatedJson;
|
|
@@ -22003,16 +22115,19 @@ function makeParseRetryStrategy(opts) {
|
|
|
22003
22115
|
const isTruncated = checkTruncated(ctx.lastOutput);
|
|
22004
22116
|
const nextPrompt = isTruncated ? opts.prompts.truncated() : opts.prompts.invalid();
|
|
22005
22117
|
const logger = opts._logger ?? getSafeLogger();
|
|
22118
|
+
const preview = opts.outputPreviewBytes && opts.outputPreviewBytes > 0 ? { outputPreview: previewOutput(ctx.lastOutput, opts.outputPreviewBytes) } : {};
|
|
22006
22119
|
if (isTruncated) {
|
|
22007
22120
|
logger?.warn(opts.reviewerKind, "JSON parse retry \u2014 likely truncated", {
|
|
22008
22121
|
storyId: ctx.storyId,
|
|
22009
22122
|
originalByteSize: ctx.lastOutput.length,
|
|
22123
|
+
...preview,
|
|
22010
22124
|
...opts.logContext
|
|
22011
22125
|
});
|
|
22012
22126
|
} else {
|
|
22013
22127
|
logger?.warn(opts.reviewerKind, "JSON parse retry \u2014 invalid shape", {
|
|
22014
22128
|
storyId: ctx.storyId,
|
|
22015
22129
|
originalByteSize: ctx.lastOutput.length,
|
|
22130
|
+
...preview,
|
|
22016
22131
|
...opts.logContext
|
|
22017
22132
|
});
|
|
22018
22133
|
}
|
|
@@ -22544,9 +22659,16 @@ async function detectTestFramework(_workdir, language, pkg) {
|
|
|
22544
22659
|
}
|
|
22545
22660
|
return;
|
|
22546
22661
|
}
|
|
22662
|
+
function clearLanguageCache() {
|
|
22663
|
+
_languageCache.clear();
|
|
22664
|
+
}
|
|
22547
22665
|
async function detectLanguage(packageDir) {
|
|
22666
|
+
if (_languageCache.has(packageDir))
|
|
22667
|
+
return _languageCache.get(packageDir);
|
|
22548
22668
|
const pkg = await _detectorDeps.readJson(join5(packageDir, "package.json"));
|
|
22549
|
-
|
|
22669
|
+
const result = await _detectLanguageImpl(packageDir, pkg);
|
|
22670
|
+
_languageCache.set(packageDir, result);
|
|
22671
|
+
return result;
|
|
22550
22672
|
}
|
|
22551
22673
|
async function detectLintTool(workdir, language) {
|
|
22552
22674
|
if (language === "go")
|
|
@@ -22574,7 +22696,7 @@ async function detectProjectProfile(workdir, existing) {
|
|
|
22574
22696
|
const lintTool = existing.lintTool !== undefined ? existing.lintTool : await detectLintTool(workdir, language);
|
|
22575
22697
|
return { language, type, testFramework, lintTool };
|
|
22576
22698
|
}
|
|
22577
|
-
var _detectorDeps, WEB_DEPS, API_DEPS;
|
|
22699
|
+
var _detectorDeps, WEB_DEPS, API_DEPS, _languageCache;
|
|
22578
22700
|
var init_detector = __esm(() => {
|
|
22579
22701
|
_detectorDeps = {
|
|
22580
22702
|
async fileExists(path) {
|
|
@@ -22595,6 +22717,7 @@ var init_detector = __esm(() => {
|
|
|
22595
22717
|
};
|
|
22596
22718
|
WEB_DEPS = new Set(["react", "next", "vue", "nuxt"]);
|
|
22597
22719
|
API_DEPS = new Set(["express", "fastify", "hono"]);
|
|
22720
|
+
_languageCache = new Map;
|
|
22598
22721
|
});
|
|
22599
22722
|
|
|
22600
22723
|
// src/test-runners/conventions.ts
|
|
@@ -23723,7 +23846,10 @@ async function detectNaxMonoLayout(workdir) {
|
|
|
23723
23846
|
} catch {}
|
|
23724
23847
|
return dirs;
|
|
23725
23848
|
}
|
|
23726
|
-
|
|
23849
|
+
function clearWorkspaceCache() {
|
|
23850
|
+
_workspaceCache.clear();
|
|
23851
|
+
}
|
|
23852
|
+
async function discoverWorkspacePackagesUncached(workdir) {
|
|
23727
23853
|
const [fromPnpm, fromNpm, fromLerna, fromTurboNx, fromNaxMono] = await Promise.all([
|
|
23728
23854
|
detectPnpmWorkspace(workdir),
|
|
23729
23855
|
detectNpmWorkspaces(workdir),
|
|
@@ -23742,7 +23868,14 @@ async function discoverWorkspacePackages(workdir) {
|
|
|
23742
23868
|
}
|
|
23743
23869
|
return unique;
|
|
23744
23870
|
}
|
|
23745
|
-
|
|
23871
|
+
async function discoverWorkspacePackages(workdir) {
|
|
23872
|
+
if (_workspaceCache.has(workdir))
|
|
23873
|
+
return _workspaceCache.get(workdir) ?? [];
|
|
23874
|
+
const result = await discoverWorkspacePackagesUncached(workdir);
|
|
23875
|
+
_workspaceCache.set(workdir, result);
|
|
23876
|
+
return result;
|
|
23877
|
+
}
|
|
23878
|
+
var _workspaceDeps, _workspaceCache;
|
|
23746
23879
|
var init_workspace = __esm(() => {
|
|
23747
23880
|
init_logger2();
|
|
23748
23881
|
_workspaceDeps = {
|
|
@@ -23755,6 +23888,7 @@ var init_workspace = __esm(() => {
|
|
|
23755
23888
|
spawn: Bun.spawn,
|
|
23756
23889
|
glob: (pattern, cwd) => new Bun.Glob(pattern).scan({ cwd, onlyFiles: false })
|
|
23757
23890
|
};
|
|
23891
|
+
_workspaceCache = new Map;
|
|
23758
23892
|
});
|
|
23759
23893
|
|
|
23760
23894
|
// src/test-runners/detect.ts
|
|
@@ -24618,6 +24752,19 @@ var init_path_filters = __esm(() => {
|
|
|
24618
24752
|
|
|
24619
24753
|
// src/verification/smart-runner.ts
|
|
24620
24754
|
import { join as join8, relative as relative3 } from "path";
|
|
24755
|
+
function clearGitRootCache() {
|
|
24756
|
+
_gitRootCache.clear();
|
|
24757
|
+
}
|
|
24758
|
+
async function getGitRootMemo(workdir) {
|
|
24759
|
+
const cached2 = _gitRootCache.get(workdir);
|
|
24760
|
+
if (cached2 !== undefined)
|
|
24761
|
+
return cached2;
|
|
24762
|
+
const result = await _gitUtilDeps.getGitRoot(workdir);
|
|
24763
|
+
if (result !== null && result !== undefined) {
|
|
24764
|
+
_gitRootCache.set(workdir, result);
|
|
24765
|
+
}
|
|
24766
|
+
return result ?? null;
|
|
24767
|
+
}
|
|
24621
24768
|
function extractPatternSuffix(pattern) {
|
|
24622
24769
|
const lastStar = pattern.lastIndexOf("*");
|
|
24623
24770
|
if (lastStar === -1)
|
|
@@ -24637,28 +24784,28 @@ async function importGrepFallback(sourceFiles, workdir, testFilePatterns) {
|
|
|
24637
24784
|
return [];
|
|
24638
24785
|
const searchTerms = sourceFiles.flatMap(extractSearchTerms);
|
|
24639
24786
|
const testFilePaths = [];
|
|
24640
|
-
|
|
24641
|
-
const
|
|
24642
|
-
|
|
24643
|
-
|
|
24787
|
+
outer:
|
|
24788
|
+
for (const pattern of testFilePatterns) {
|
|
24789
|
+
const g = _bunDeps.glob(pattern);
|
|
24790
|
+
for await (const file3 of g.scan(workdir)) {
|
|
24791
|
+
testFilePaths.push(`${workdir}/${file3}`);
|
|
24792
|
+
if (testFilePaths.length >= MAX_GREP_TEST_FILES) {
|
|
24793
|
+
getSafeLogger()?.debug("smart-runner", "import-grep glob cap reached \u2014 results truncated", {
|
|
24794
|
+
cap: MAX_GREP_TEST_FILES
|
|
24795
|
+
});
|
|
24796
|
+
break outer;
|
|
24797
|
+
}
|
|
24798
|
+
}
|
|
24644
24799
|
}
|
|
24645
|
-
|
|
24646
|
-
const matched = [];
|
|
24647
|
-
for (const testFile of testFilePaths) {
|
|
24648
|
-
let content;
|
|
24800
|
+
const results = await Promise.all(testFilePaths.map(async (testFile) => {
|
|
24649
24801
|
try {
|
|
24650
|
-
content = await _bunDeps.file(testFile).text();
|
|
24802
|
+
const content = await _bunDeps.file(testFile).text();
|
|
24803
|
+
return searchTerms.some((t) => content.includes(t)) ? testFile : null;
|
|
24651
24804
|
} catch {
|
|
24652
|
-
|
|
24653
|
-
}
|
|
24654
|
-
for (const term of searchTerms) {
|
|
24655
|
-
if (content.includes(term)) {
|
|
24656
|
-
matched.push(testFile);
|
|
24657
|
-
break;
|
|
24658
|
-
}
|
|
24805
|
+
return null;
|
|
24659
24806
|
}
|
|
24660
|
-
}
|
|
24661
|
-
return
|
|
24807
|
+
}));
|
|
24808
|
+
return results.filter((p) => p !== null);
|
|
24662
24809
|
}
|
|
24663
24810
|
async function mapSourceToTests(sourceFiles, workdir, packagePrefix, testFilePatterns = [...DEFAULT_TEST_FILE_PATTERNS]) {
|
|
24664
24811
|
const testSuffixes = [...new Set(testFilePatterns.map(extractPatternSuffix).filter((s) => s !== null))];
|
|
@@ -24683,11 +24830,11 @@ async function mapSourceToTests(sourceFiles, workdir, packagePrefix, testFilePat
|
|
|
24683
24830
|
}
|
|
24684
24831
|
candidates.push(`${workdir}/${sourceWithoutExt}${suffix}`);
|
|
24685
24832
|
}
|
|
24686
|
-
|
|
24687
|
-
|
|
24688
|
-
|
|
24689
|
-
|
|
24690
|
-
}
|
|
24833
|
+
const existsFlags = await Promise.all(candidates.map((c) => _bunDeps.file(c).exists()));
|
|
24834
|
+
candidates.forEach((c, i) => {
|
|
24835
|
+
if (existsFlags[i])
|
|
24836
|
+
result.push(c);
|
|
24837
|
+
});
|
|
24691
24838
|
}
|
|
24692
24839
|
return result;
|
|
24693
24840
|
}
|
|
@@ -24726,7 +24873,7 @@ async function getChangedNonTestFiles(workdir, baseRef, packagePrefix, testFileR
|
|
|
24726
24873
|
const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(effectiveRepoRoot, packageDir);
|
|
24727
24874
|
let effectivePrefix = packagePrefix;
|
|
24728
24875
|
if (packagePrefix && repoRoot) {
|
|
24729
|
-
const gitRoot = await
|
|
24876
|
+
const gitRoot = await getGitRootMemo(workdir);
|
|
24730
24877
|
const extraPrefix2 = gitRoot && gitRoot !== repoRoot ? relative3(gitRoot, repoRoot) : "";
|
|
24731
24878
|
effectivePrefix = extraPrefix2 ? `${extraPrefix2}/${packagePrefix}` : packagePrefix;
|
|
24732
24879
|
}
|
|
@@ -24753,7 +24900,7 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
|
|
|
24753
24900
|
`).filter(Boolean);
|
|
24754
24901
|
const packageDir = packagePrefix ? join8(repoRoot, packagePrefix) : undefined;
|
|
24755
24902
|
const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(repoRoot, packageDir);
|
|
24756
|
-
const gitRoot = await
|
|
24903
|
+
const gitRoot = await getGitRootMemo(workdir);
|
|
24757
24904
|
const extraPrefix = gitRoot && gitRoot !== repoRoot ? relative3(gitRoot, repoRoot) : "";
|
|
24758
24905
|
const effectivePrefix = packagePrefix ? extraPrefix ? `${extraPrefix}/${packagePrefix}` : packagePrefix : undefined;
|
|
24759
24906
|
const scopedRaw = effectivePrefix ? lines.filter((f) => f.startsWith(`${effectivePrefix}/`)) : lines;
|
|
@@ -24764,8 +24911,9 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
|
|
|
24764
24911
|
return [];
|
|
24765
24912
|
}
|
|
24766
24913
|
}
|
|
24767
|
-
var _bunDeps, _gitUtilDeps, _smartRunnerDeps;
|
|
24914
|
+
var _bunDeps, MAX_GREP_TEST_FILES = 200, _gitUtilDeps, _gitRootCache, _smartRunnerDeps;
|
|
24768
24915
|
var init_smart_runner = __esm(() => {
|
|
24916
|
+
init_logger2();
|
|
24769
24917
|
init_conventions();
|
|
24770
24918
|
init_git();
|
|
24771
24919
|
init_path_filters();
|
|
@@ -24776,6 +24924,7 @@ var init_smart_runner = __esm(() => {
|
|
|
24776
24924
|
_gitUtilDeps = {
|
|
24777
24925
|
getGitRoot
|
|
24778
24926
|
};
|
|
24927
|
+
_gitRootCache = new Map;
|
|
24779
24928
|
_smartRunnerDeps = {
|
|
24780
24929
|
glob: _bunDeps.glob,
|
|
24781
24930
|
file: _bunDeps.file,
|
|
@@ -25133,31 +25282,50 @@ async function resolveSourceGlob(override, packageDir) {
|
|
|
25133
25282
|
const language = await _codeNeighborDeps.detectLanguage(packageDir);
|
|
25134
25283
|
return (language && SOURCE_GLOB_BY_LANGUAGE[language]) ?? FALLBACK_SOURCE_GLOB;
|
|
25135
25284
|
}
|
|
25136
|
-
|
|
25285
|
+
function scanDirectory(sourceGlob, workdir, ignoreMatchers, maxGlobFiles, globCtx) {
|
|
25286
|
+
const { files, truncated } = _codeNeighborDeps.glob(sourceGlob, workdir, ignoreMatchers, maxGlobFiles, globCtx);
|
|
25287
|
+
return { workdir, files, truncated };
|
|
25288
|
+
}
|
|
25289
|
+
async function readCached(absolutePath, cache) {
|
|
25290
|
+
const cached2 = cache.get(absolutePath);
|
|
25291
|
+
if (cached2 !== undefined)
|
|
25292
|
+
return cached2;
|
|
25293
|
+
try {
|
|
25294
|
+
const content = await _codeNeighborDeps.readFile(absolutePath);
|
|
25295
|
+
cache.set(absolutePath, content);
|
|
25296
|
+
return content;
|
|
25297
|
+
} catch {
|
|
25298
|
+
cache.set(absolutePath, "");
|
|
25299
|
+
return null;
|
|
25300
|
+
}
|
|
25301
|
+
}
|
|
25302
|
+
async function collectNeighbors(filePath, workdir, scannedDirs, contentCache, siblingTestContext) {
|
|
25137
25303
|
const neighbors = new Set;
|
|
25138
25304
|
let anyTruncated = false;
|
|
25139
|
-
|
|
25140
|
-
|
|
25141
|
-
|
|
25142
|
-
|
|
25143
|
-
|
|
25144
|
-
|
|
25305
|
+
const ownAbsPath = join10(workdir, filePath);
|
|
25306
|
+
if (await _codeNeighborDeps.fileExists(ownAbsPath)) {
|
|
25307
|
+
const ownContent = await readCached(ownAbsPath, contentCache);
|
|
25308
|
+
if (ownContent !== null && ownContent.length > 0) {
|
|
25309
|
+
for (const spec of parseImportSpecifiers(ownContent)) {
|
|
25310
|
+
const resolved = resolveImport(spec, filePath, workdir);
|
|
25311
|
+
if (resolved && resolved !== filePath)
|
|
25312
|
+
neighbors.add(resolved);
|
|
25313
|
+
}
|
|
25145
25314
|
}
|
|
25146
25315
|
}
|
|
25147
25316
|
const fileBaseName = (filePath.split("/").pop() ?? filePath).replace(/\.[^.]+$/, "");
|
|
25148
25317
|
const fileNoExt = filePath.replace(/\.[^.]+$/, "");
|
|
25149
|
-
|
|
25150
|
-
const { files: srcFiles, truncated }
|
|
25151
|
-
|
|
25152
|
-
|
|
25153
|
-
|
|
25154
|
-
|
|
25155
|
-
|
|
25156
|
-
|
|
25157
|
-
|
|
25158
|
-
|
|
25159
|
-
|
|
25160
|
-
if (content.includes(fileBaseName)) {
|
|
25318
|
+
outer:
|
|
25319
|
+
for (const { workdir: scanWorkdir, files: srcFiles, truncated } of scannedDirs) {
|
|
25320
|
+
if (truncated)
|
|
25321
|
+
anyTruncated = true;
|
|
25322
|
+
for (const srcFile of srcFiles) {
|
|
25323
|
+
if (neighbors.size >= MAX_NEIGHBORS_PER_FILE)
|
|
25324
|
+
break outer;
|
|
25325
|
+
if (srcFile === filePath)
|
|
25326
|
+
continue;
|
|
25327
|
+
const content = await readCached(join10(scanWorkdir, srcFile), contentCache);
|
|
25328
|
+
if (content?.includes(fileBaseName)) {
|
|
25161
25329
|
for (const spec of parseImportSpecifiers(content)) {
|
|
25162
25330
|
const resolved = resolveImport(spec, srcFile, scanWorkdir);
|
|
25163
25331
|
if (resolved === filePath || resolved === fileNoExt) {
|
|
@@ -25166,17 +25334,8 @@ async function collectNeighbors(filePath, workdir, sourceGlob, maxGlobFiles, ext
|
|
|
25166
25334
|
}
|
|
25167
25335
|
}
|
|
25168
25336
|
}
|
|
25169
|
-
}
|
|
25170
|
-
}
|
|
25171
|
-
};
|
|
25172
|
-
await scanForReverseDeps(workdir);
|
|
25173
|
-
if (extraGlobWorkdirs) {
|
|
25174
|
-
for (const extraDir of extraGlobWorkdirs) {
|
|
25175
|
-
if (neighbors.size >= MAX_NEIGHBORS_PER_FILE)
|
|
25176
|
-
break;
|
|
25177
|
-
await scanForReverseDeps(extraDir);
|
|
25337
|
+
}
|
|
25178
25338
|
}
|
|
25179
|
-
}
|
|
25180
25339
|
if (siblingTestContext && !isTestFile2(filePath, siblingTestContext.regex)) {
|
|
25181
25340
|
const candidates = deriveSiblingTestCandidates(filePath, siblingTestContext.globs);
|
|
25182
25341
|
let chosen = null;
|
|
@@ -25239,10 +25398,17 @@ class CodeNeighborProvider {
|
|
|
25239
25398
|
const ignoreMatchers = request.naxIgnoreIndex?.getMatchers(workdir);
|
|
25240
25399
|
const sourceGlob = await resolveSourceGlob(this.sourceGlobOverride, request.packageDir);
|
|
25241
25400
|
const globCtx = { storyId: request.storyId, packageDir: request.packageDir };
|
|
25401
|
+
const scannedDirs = [scanDirectory(sourceGlob, workdir, ignoreMatchers, this.maxGlobFiles, globCtx)];
|
|
25402
|
+
if (extraGlobWorkdirs) {
|
|
25403
|
+
for (const extraDir of extraGlobWorkdirs) {
|
|
25404
|
+
scannedDirs.push(scanDirectory(sourceGlob, extraDir, ignoreMatchers, this.maxGlobFiles, globCtx));
|
|
25405
|
+
}
|
|
25406
|
+
}
|
|
25407
|
+
const contentCache = new Map;
|
|
25242
25408
|
const sections = [];
|
|
25243
25409
|
let anyTruncated = false;
|
|
25244
25410
|
for (const file3 of filesToProcess) {
|
|
25245
|
-
const { neighbors, truncated } = await collectNeighbors(file3, workdir,
|
|
25411
|
+
const { neighbors, truncated } = await collectNeighbors(file3, workdir, scannedDirs, contentCache, siblingTestContext);
|
|
25246
25412
|
if (truncated)
|
|
25247
25413
|
anyTruncated = true;
|
|
25248
25414
|
if (neighbors.length > 0) {
|
|
@@ -26160,13 +26326,25 @@ function buildPullToolDescriptors(stageToolNames, pullConfig) {
|
|
|
26160
26326
|
const allowed = pullConfig.allowedTools;
|
|
26161
26327
|
return stageToolNames.filter((name) => allowed.length === 0 || allowed.includes(name)).map((name) => PULL_TOOL_REGISTRY[name]).filter((d) => d !== undefined).map((d) => ({ ...d, maxCallsPerSession: pullConfig.maxCallsPerSession ?? d.maxCallsPerSession }));
|
|
26162
26328
|
}
|
|
26163
|
-
async function fetchWithTimeout(provider, request) {
|
|
26329
|
+
async function fetchWithTimeout(provider, request, timeoutMs = PROVIDER_FETCH_TIMEOUT_MS) {
|
|
26330
|
+
const controller = new AbortController;
|
|
26164
26331
|
let handle;
|
|
26332
|
+
let timedOut = false;
|
|
26165
26333
|
const timeout = new Promise((_, reject) => {
|
|
26166
|
-
handle = setTimeout(() =>
|
|
26334
|
+
handle = setTimeout(() => {
|
|
26335
|
+
timedOut = true;
|
|
26336
|
+
controller.abort();
|
|
26337
|
+
reject(new Error(`Provider "${provider.id}" timed out`));
|
|
26338
|
+
}, timeoutMs);
|
|
26339
|
+
});
|
|
26340
|
+
const fetchPromise = provider.fetch(request, controller.signal).then((result) => result, (err) => {
|
|
26341
|
+
if (timedOut) {
|
|
26342
|
+
return new Promise(() => {});
|
|
26343
|
+
}
|
|
26344
|
+
throw err;
|
|
26167
26345
|
});
|
|
26168
26346
|
try {
|
|
26169
|
-
return await Promise.race([
|
|
26347
|
+
return await Promise.race([fetchPromise, timeout]);
|
|
26170
26348
|
} finally {
|
|
26171
26349
|
clearTimeout(handle);
|
|
26172
26350
|
}
|
|
@@ -26805,12 +26983,7 @@ class GitHistoryProvider {
|
|
|
26805
26983
|
return { chunks: [], pullTools: [] };
|
|
26806
26984
|
}
|
|
26807
26985
|
const filesToProcess = touchedFiles.filter(isRelativeAndSafe).slice(0, MAX_FILES2);
|
|
26808
|
-
const sections =
|
|
26809
|
-
for (const file3 of filesToProcess) {
|
|
26810
|
-
const section = await fetchFileHistory(file3, workdir);
|
|
26811
|
-
if (section)
|
|
26812
|
-
sections.push(section);
|
|
26813
|
-
}
|
|
26986
|
+
const sections = (await Promise.all(filesToProcess.map((file3) => fetchFileHistory(file3, workdir)))).filter((section) => section !== null);
|
|
26814
26987
|
if (sections.length === 0) {
|
|
26815
26988
|
return { chunks: [], pullTools: [] };
|
|
26816
26989
|
}
|
|
@@ -33270,20 +33443,23 @@ function createDrainDeadline(deadlineMs) {
|
|
|
33270
33443
|
};
|
|
33271
33444
|
}
|
|
33272
33445
|
async function runQualityCommand(opts) {
|
|
33273
|
-
const { commandName, command, workdir, storyId, timeoutMs = DEFAULT_TIMEOUT_MS, env: env2 } = opts;
|
|
33446
|
+
const { commandName, command, workdir, storyId, timeoutMs = DEFAULT_TIMEOUT_MS, env: env2, stripEnvVars } = opts;
|
|
33274
33447
|
const startTime = Date.now();
|
|
33275
33448
|
const logger = getSafeLogger();
|
|
33276
33449
|
logger?.info("quality", `Running ${commandName}`, { storyId, commandName, command, workdir });
|
|
33277
33450
|
try {
|
|
33451
|
+
const baseEnv = {
|
|
33452
|
+
...process.env
|
|
33453
|
+
};
|
|
33454
|
+
for (const key of stripEnvVars ?? []) {
|
|
33455
|
+
delete baseEnv[key];
|
|
33456
|
+
}
|
|
33278
33457
|
const proc = _qualityRunnerDeps.spawn({
|
|
33279
33458
|
cmd: ["/bin/sh", "-c", command],
|
|
33280
33459
|
cwd: workdir,
|
|
33281
33460
|
stdout: "pipe",
|
|
33282
33461
|
stderr: "pipe",
|
|
33283
|
-
env: {
|
|
33284
|
-
...process.env,
|
|
33285
|
-
...env2 ?? {}
|
|
33286
|
-
}
|
|
33462
|
+
env: { ...baseEnv, ...env2 ?? {} }
|
|
33287
33463
|
});
|
|
33288
33464
|
let timedOut = false;
|
|
33289
33465
|
let exitedBeforeSigkill = false;
|
|
@@ -36863,7 +37039,7 @@ function buildVerifierFindings(verdict, categorization) {
|
|
|
36863
37039
|
return [];
|
|
36864
37040
|
}
|
|
36865
37041
|
}
|
|
36866
|
-
function parseVerdictFromStdout(output,
|
|
37042
|
+
function parseVerdictFromStdout(output, input, _ctx) {
|
|
36867
37043
|
if (!output || !output.trim()) {
|
|
36868
37044
|
throw new ParseValidationError("verifier produced no stdout");
|
|
36869
37045
|
}
|
|
@@ -36876,6 +37052,18 @@ function parseVerdictFromStdout(output, _input, _ctx) {
|
|
|
36876
37052
|
throw new ParseValidationError("verifier stdout JSON missing required VerifierVerdict fields");
|
|
36877
37053
|
}
|
|
36878
37054
|
const categorization = categorizeVerdict(verdict, verdict.tests.allPassing === true);
|
|
37055
|
+
getSafeLogger()?.info("verifier", "Verdict categorized", {
|
|
37056
|
+
storyId: input.story.id,
|
|
37057
|
+
approved: verdict.approved,
|
|
37058
|
+
success: categorization.success,
|
|
37059
|
+
advisoryOverride: verdict.approved === false && categorization.success,
|
|
37060
|
+
testsPassing: verdict.tests.allPassing,
|
|
37061
|
+
passCount: verdict.tests.passCount,
|
|
37062
|
+
failCount: verdict.tests.failCount,
|
|
37063
|
+
testModsDetected: verdict.testModifications.detected,
|
|
37064
|
+
testModsLegitimate: verdict.testModifications.legitimate,
|
|
37065
|
+
...categorization.failureCategory && { failureCategory: categorization.failureCategory }
|
|
37066
|
+
});
|
|
36879
37067
|
return {
|
|
36880
37068
|
success: categorization.success,
|
|
36881
37069
|
filesChanged: [],
|
|
@@ -36897,6 +37085,7 @@ var verifierOp;
|
|
|
36897
37085
|
var init_verify = __esm(() => {
|
|
36898
37086
|
init_retry();
|
|
36899
37087
|
init_config();
|
|
37088
|
+
init_logger2();
|
|
36900
37089
|
init_tdd_builder();
|
|
36901
37090
|
init_isolation();
|
|
36902
37091
|
init_verdict();
|
|
@@ -36915,6 +37104,7 @@ var init_verify = __esm(() => {
|
|
|
36915
37104
|
},
|
|
36916
37105
|
reviewerKind: "verifier",
|
|
36917
37106
|
maxAttempts: 2,
|
|
37107
|
+
outputPreviewBytes: 600,
|
|
36918
37108
|
prompts: {
|
|
36919
37109
|
invalid: () => TddPromptBuilder.verdictRetry(),
|
|
36920
37110
|
truncated: () => TddPromptBuilder.verdictRetryCondensed()
|
|
@@ -36943,24 +37133,38 @@ var init_verify = __esm(() => {
|
|
|
36943
37133
|
},
|
|
36944
37134
|
async recover(input, verifyCtx) {
|
|
36945
37135
|
const packageDir = verifyCtx.packageView.packageDir;
|
|
37136
|
+
const logger = getSafeLogger();
|
|
37137
|
+
const storyId = input.story.id;
|
|
36946
37138
|
try {
|
|
36947
37139
|
const verdict = await readVerdict(packageDir);
|
|
36948
37140
|
if (verdict) {
|
|
36949
37141
|
const testsAllPassing = verdict.tests.allPassing === true;
|
|
36950
37142
|
const categorization = categorizeVerdict(verdict, testsAllPassing);
|
|
36951
37143
|
const isolation = await runVerifierIsolation(input.beforeRef, verifyCtx);
|
|
37144
|
+
const normalizedFindings = buildVerifierFindings(verdict, categorization);
|
|
37145
|
+
logger?.warn("verifier", "Recovered verdict from disk after unparseable stdout", {
|
|
37146
|
+
storyId,
|
|
37147
|
+
packageDir,
|
|
37148
|
+
success: categorization.success,
|
|
37149
|
+
findingsCount: normalizedFindings.length,
|
|
37150
|
+
...categorization.failureCategory && { failureCategory: categorization.failureCategory }
|
|
37151
|
+
});
|
|
36952
37152
|
return {
|
|
36953
37153
|
success: categorization.success,
|
|
36954
37154
|
filesChanged: [],
|
|
36955
37155
|
estimatedCostUsd: 0,
|
|
36956
37156
|
durationMs: 0,
|
|
36957
37157
|
output: "",
|
|
36958
|
-
normalizedFindings
|
|
37158
|
+
normalizedFindings,
|
|
36959
37159
|
...categorization.failureCategory && { failureCategory: categorization.failureCategory },
|
|
36960
37160
|
...categorization.reviewReason && { reviewReason: categorization.reviewReason },
|
|
36961
37161
|
...isolation && { isolation }
|
|
36962
37162
|
};
|
|
36963
37163
|
}
|
|
37164
|
+
logger?.error("verifier", "No usable verdict \u2014 unparseable stdout and no verdict file on disk (fail-closed)", {
|
|
37165
|
+
storyId,
|
|
37166
|
+
packageDir
|
|
37167
|
+
});
|
|
36964
37168
|
return {
|
|
36965
37169
|
success: false,
|
|
36966
37170
|
filesChanged: [],
|
|
@@ -38212,7 +38416,8 @@ var init_mechanical_lintfix_strategy = __esm(() => {
|
|
|
38212
38416
|
commandName: "lintFix",
|
|
38213
38417
|
command,
|
|
38214
38418
|
workdir: input.workdir,
|
|
38215
|
-
storyId: input.storyId
|
|
38419
|
+
storyId: input.storyId,
|
|
38420
|
+
stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
|
|
38216
38421
|
});
|
|
38217
38422
|
return { applied: true, exitCode: result.exitCode };
|
|
38218
38423
|
}
|
|
@@ -38270,7 +38475,8 @@ var init_mechanical_formatfix_strategy = __esm(() => {
|
|
|
38270
38475
|
commandName: "formatFix",
|
|
38271
38476
|
command,
|
|
38272
38477
|
workdir: input.workdir,
|
|
38273
|
-
storyId: input.storyId
|
|
38478
|
+
storyId: input.storyId,
|
|
38479
|
+
stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
|
|
38274
38480
|
});
|
|
38275
38481
|
return { applied: true, exitCode: result.exitCode };
|
|
38276
38482
|
}
|
|
@@ -38303,7 +38509,8 @@ var init_lint_check = __esm(() => {
|
|
|
38303
38509
|
commandName: "lint",
|
|
38304
38510
|
command: command ?? "",
|
|
38305
38511
|
workdir: input.workdir,
|
|
38306
|
-
storyId: input.storyId
|
|
38512
|
+
storyId: input.storyId,
|
|
38513
|
+
stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
|
|
38307
38514
|
});
|
|
38308
38515
|
if (result.exitCode === 0) {
|
|
38309
38516
|
return { success: true, findings: [], durationMs: Date.now() - start };
|
|
@@ -38552,7 +38759,8 @@ var init_typecheck_check = __esm(() => {
|
|
|
38552
38759
|
commandName: "typecheck",
|
|
38553
38760
|
command: command ?? "",
|
|
38554
38761
|
workdir: input.workdir,
|
|
38555
|
-
storyId: input.storyId
|
|
38762
|
+
storyId: input.storyId,
|
|
38763
|
+
stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
|
|
38556
38764
|
});
|
|
38557
38765
|
if (result.exitCode === 0) {
|
|
38558
38766
|
return { success: true, findings: [], durationMs: Date.now() - start };
|
|
@@ -39527,13 +39735,14 @@ async function resolveLintScope(args) {
|
|
|
39527
39735
|
function resolveScopedTemplate(reviewCommands, qualityCommands) {
|
|
39528
39736
|
return reviewCommands.lintScoped ?? qualityCommands?.lintScoped;
|
|
39529
39737
|
}
|
|
39530
|
-
async function runLintCommand(workdir, storyId, env2, command) {
|
|
39738
|
+
async function runLintCommand(workdir, storyId, env2, command, stripEnvVars) {
|
|
39531
39739
|
return runQualityCommand({
|
|
39532
39740
|
commandName: SCOPED_LINT_CHECK,
|
|
39533
39741
|
command,
|
|
39534
39742
|
workdir,
|
|
39535
39743
|
storyId,
|
|
39536
|
-
env: env2
|
|
39744
|
+
env: env2,
|
|
39745
|
+
stripEnvVars
|
|
39537
39746
|
});
|
|
39538
39747
|
}
|
|
39539
39748
|
function toReviewCheck(result) {
|
|
@@ -39574,7 +39783,7 @@ async function runScopedLintCheck(args) {
|
|
|
39574
39783
|
storyId: args.storyId,
|
|
39575
39784
|
reason: scope.degradedReason
|
|
39576
39785
|
});
|
|
39577
|
-
const fullResult2 = await _scopedLintDeps.runLintCommand(args.workdir, args.storyId, args.env, fullLintCommand);
|
|
39786
|
+
const fullResult2 = await _scopedLintDeps.runLintCommand(args.workdir, args.storyId, args.env, fullLintCommand, args.stripEnvVars);
|
|
39578
39787
|
return withLintScope(attachLintFindings(toReviewCheck(fullResult2), args.lintOutputFormat, args.workdir), scope, "degraded");
|
|
39579
39788
|
}
|
|
39580
39789
|
logger?.info("review", "lint_scope_empty", { storyId: args.storyId });
|
|
@@ -39599,19 +39808,19 @@ async function runScopedLintCheck(args) {
|
|
|
39599
39808
|
}
|
|
39600
39809
|
if (scopedTemplate) {
|
|
39601
39810
|
const scopedCommand = scopedTemplate.replaceAll("{{files}}", scope.files.map(shellQuotePath4).join(" "));
|
|
39602
|
-
const scopedResult = await _scopedLintDeps.runLintCommand(args.workdir, args.storyId, args.env, scopedCommand);
|
|
39811
|
+
const scopedResult = await _scopedLintDeps.runLintCommand(args.workdir, args.storyId, args.env, scopedCommand, args.stripEnvVars);
|
|
39603
39812
|
return withLintScope(attachLintFindings(toReviewCheck(scopedResult), args.lintOutputFormat, args.workdir), scope);
|
|
39604
39813
|
}
|
|
39605
39814
|
if (!scope.degradedReason && isSupportedDerivedScopedCommand(fullLintCommand)) {
|
|
39606
39815
|
const scopedCommand = appendFilesToCommand(fullLintCommand, scope.files);
|
|
39607
|
-
const scopedResult = await _scopedLintDeps.runLintCommand(args.workdir, args.storyId, args.env, scopedCommand);
|
|
39816
|
+
const scopedResult = await _scopedLintDeps.runLintCommand(args.workdir, args.storyId, args.env, scopedCommand, args.stripEnvVars);
|
|
39608
39817
|
return withLintScope(attachLintFindings(toReviewCheck(scopedResult), args.lintOutputFormat, args.workdir), scope);
|
|
39609
39818
|
}
|
|
39610
39819
|
logger?.warn("review", "lint_scope_degraded", {
|
|
39611
39820
|
storyId: args.storyId,
|
|
39612
39821
|
reason: scope.degradedReason ?? "unsupported_scoped_command_shape"
|
|
39613
39822
|
});
|
|
39614
|
-
const fullResult = await _scopedLintDeps.runLintCommand(args.workdir, args.storyId, args.env, fullLintCommand);
|
|
39823
|
+
const fullResult = await _scopedLintDeps.runLintCommand(args.workdir, args.storyId, args.env, fullLintCommand, args.stripEnvVars);
|
|
39615
39824
|
if (fullResult.exitCode === 0)
|
|
39616
39825
|
return withLintScope(toReviewCheck(fullResult), scope, "degraded");
|
|
39617
39826
|
const parsed = parseLintOutput(fullResult.output, args.lintOutputFormat ?? "auto", {
|
|
@@ -40337,8 +40546,8 @@ async function resolveCommand(check2, config2, executionConfig, workdir, quality
|
|
|
40337
40546
|
}
|
|
40338
40547
|
return null;
|
|
40339
40548
|
}
|
|
40340
|
-
async function runCheck(check2, command, workdir, storyId, env2) {
|
|
40341
|
-
const result = await runQualityCommand({ commandName: check2, command, workdir, storyId, env: env2 });
|
|
40549
|
+
async function runCheck(check2, command, workdir, storyId, env2, stripEnvVars) {
|
|
40550
|
+
const result = await runQualityCommand({ commandName: check2, command, workdir, storyId, env: env2, stripEnvVars });
|
|
40342
40551
|
return {
|
|
40343
40552
|
check: check2,
|
|
40344
40553
|
command: result.command,
|
|
@@ -40544,8 +40753,9 @@ async function runReview(opts) {
|
|
|
40544
40753
|
story,
|
|
40545
40754
|
storyGitRef,
|
|
40546
40755
|
env: env2,
|
|
40756
|
+
stripEnvVars: naxConfig?.quality?.stripEnvVars ?? [],
|
|
40547
40757
|
naxIgnoreIndex
|
|
40548
|
-
}) : normalizeMechanicalFindings(checkName, await runCheck(checkName, command, workdir, storyId, env2), workdir);
|
|
40758
|
+
}) : normalizeMechanicalFindings(checkName, await runCheck(checkName, command, workdir, storyId, env2, naxConfig?.quality?.stripEnvVars ?? []), workdir);
|
|
40549
40759
|
checks3.push(result);
|
|
40550
40760
|
if (result.success) {
|
|
40551
40761
|
logger?.info("review", `${checkName} passed`, {
|
|
@@ -43893,13 +44103,13 @@ var init_factory = __esm(() => {
|
|
|
43893
44103
|
|
|
43894
44104
|
// src/execution/pid-registry.ts
|
|
43895
44105
|
import { existsSync as existsSync7 } from "fs";
|
|
43896
|
-
import { appendFile as appendFile2 } from "fs/promises";
|
|
43897
44106
|
|
|
43898
44107
|
class PidRegistry {
|
|
43899
44108
|
workdir;
|
|
43900
44109
|
pidsFilePath;
|
|
43901
44110
|
pids = new Set;
|
|
43902
44111
|
frozen = false;
|
|
44112
|
+
writeQueueTail = Promise.resolve();
|
|
43903
44113
|
constructor(workdir, _platform) {
|
|
43904
44114
|
this.workdir = workdir;
|
|
43905
44115
|
this.pidsFilePath = `${workdir}/${PID_REGISTRY_FILE}`;
|
|
@@ -43920,15 +44130,8 @@ class PidRegistry {
|
|
|
43920
44130
|
return;
|
|
43921
44131
|
}
|
|
43922
44132
|
this.pids.add(pid);
|
|
43923
|
-
const entry = {
|
|
43924
|
-
pid,
|
|
43925
|
-
spawnedAt: new Date().toISOString(),
|
|
43926
|
-
workdir: this.workdir
|
|
43927
|
-
};
|
|
43928
44133
|
try {
|
|
43929
|
-
|
|
43930
|
-
`;
|
|
43931
|
-
await appendFile2(this.pidsFilePath, line);
|
|
44134
|
+
await this.enqueueWrite();
|
|
43932
44135
|
logger?.debug("pid-registry", `Registered PID ${pid}`, { pid });
|
|
43933
44136
|
} catch (err) {
|
|
43934
44137
|
logger?.warn("pid-registry", `Failed to write PID ${pid} to registry`, {
|
|
@@ -43940,7 +44143,7 @@ class PidRegistry {
|
|
|
43940
44143
|
const logger = getSafeLogger();
|
|
43941
44144
|
this.pids.delete(pid);
|
|
43942
44145
|
try {
|
|
43943
|
-
await this.
|
|
44146
|
+
await this.enqueueWrite();
|
|
43944
44147
|
logger?.debug("pid-registry", `Unregistered PID ${pid}`, { pid });
|
|
43945
44148
|
} catch (err) {
|
|
43946
44149
|
logger?.warn("pid-registry", `Failed to unregister PID ${pid}`, {
|
|
@@ -43959,8 +44162,8 @@ class PidRegistry {
|
|
|
43959
44162
|
const killPromises = pids.map((pid) => this.killPidTree(pid));
|
|
43960
44163
|
await Promise.allSettled(killPromises);
|
|
43961
44164
|
try {
|
|
43962
|
-
await Bun.write(this.pidsFilePath, "");
|
|
43963
44165
|
this.pids.clear();
|
|
44166
|
+
await this.enqueueWrite();
|
|
43964
44167
|
logger?.info("pid-registry", "All registered PIDs killed and registry cleared");
|
|
43965
44168
|
} catch (err) {
|
|
43966
44169
|
logger?.warn("pid-registry", "Failed to clear registry file", {
|
|
@@ -44179,6 +44382,37 @@ class PidRegistry {
|
|
|
44179
44382
|
getPids() {
|
|
44180
44383
|
return Array.from(this.pids);
|
|
44181
44384
|
}
|
|
44385
|
+
snapshot() {
|
|
44386
|
+
return Array.from(this.pids);
|
|
44387
|
+
}
|
|
44388
|
+
async flush() {
|
|
44389
|
+
await this.writeQueueTail;
|
|
44390
|
+
}
|
|
44391
|
+
async readPidsFromDisk() {
|
|
44392
|
+
try {
|
|
44393
|
+
if (!existsSync7(this.pidsFilePath))
|
|
44394
|
+
return [];
|
|
44395
|
+
const content = await Bun.file(this.pidsFilePath).text();
|
|
44396
|
+
return content.split(`
|
|
44397
|
+
`).filter((line) => line.trim()).map((line) => {
|
|
44398
|
+
try {
|
|
44399
|
+
return JSON.parse(line).pid;
|
|
44400
|
+
} catch {
|
|
44401
|
+
return null;
|
|
44402
|
+
}
|
|
44403
|
+
}).filter((pid) => pid !== null);
|
|
44404
|
+
} catch {
|
|
44405
|
+
return [];
|
|
44406
|
+
}
|
|
44407
|
+
}
|
|
44408
|
+
enqueueWrite() {
|
|
44409
|
+
this.writeQueueTail = this.writeQueueTail.then(() => this.writePidsFile().catch((err) => {
|
|
44410
|
+
getSafeLogger()?.warn("pid-registry", "Failed to flush PID file \u2014 on-disk registry may be stale", {
|
|
44411
|
+
error: errorMessage(err)
|
|
44412
|
+
});
|
|
44413
|
+
}));
|
|
44414
|
+
return this.writeQueueTail;
|
|
44415
|
+
}
|
|
44182
44416
|
}
|
|
44183
44417
|
var PID_REGISTRY_FILE = ".nax-pids", PID_TREE_KILL_GRACE_MS = 250, _pidRegistryDeps;
|
|
44184
44418
|
var init_pid_registry = __esm(() => {
|
|
@@ -52025,7 +52259,7 @@ var init_effectiveness = __esm(() => {
|
|
|
52025
52259
|
});
|
|
52026
52260
|
|
|
52027
52261
|
// src/execution/progress.ts
|
|
52028
|
-
import { appendFile as
|
|
52262
|
+
import { appendFile as appendFile2, mkdir as mkdir7 } from "fs/promises";
|
|
52029
52263
|
import { join as join44 } from "path";
|
|
52030
52264
|
async function appendProgress(featureDir, storyId, status, message) {
|
|
52031
52265
|
await mkdir7(featureDir, { recursive: true });
|
|
@@ -52033,7 +52267,7 @@ async function appendProgress(featureDir, storyId, status, message) {
|
|
|
52033
52267
|
const timestamp = new Date().toISOString();
|
|
52034
52268
|
const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
|
|
52035
52269
|
`;
|
|
52036
|
-
await
|
|
52270
|
+
await appendFile2(progressPath, entry);
|
|
52037
52271
|
}
|
|
52038
52272
|
var init_progress = () => {};
|
|
52039
52273
|
|
|
@@ -52140,6 +52374,7 @@ var init_completion = __esm(() => {
|
|
|
52140
52374
|
const logger = getLogger();
|
|
52141
52375
|
const isBatch = ctx.stories.length > 1;
|
|
52142
52376
|
const sessionCost = ctx.agentResult?.estimatedCostUsd || 0;
|
|
52377
|
+
const persistPrd = ctx.skipPrdPersistence !== true;
|
|
52143
52378
|
const prdPath = ctx.prdPath ?? (ctx.featureDir ? `${ctx.featureDir}/prd.json` : `${ctx.workdir}/nax/features/unknown/prd.json`);
|
|
52144
52379
|
const storyStartTime = ctx.storyStartTime || new Date().toISOString();
|
|
52145
52380
|
if (isBatch) {
|
|
@@ -52164,7 +52399,9 @@ var init_completion = __esm(() => {
|
|
|
52164
52399
|
}
|
|
52165
52400
|
}
|
|
52166
52401
|
for (const completedStory of ctx.stories) {
|
|
52167
|
-
|
|
52402
|
+
if (persistPrd) {
|
|
52403
|
+
markStoryPassed(ctx.prd, completedStory.id);
|
|
52404
|
+
}
|
|
52168
52405
|
const costPerStory = sessionCost / ctx.stories.length;
|
|
52169
52406
|
logger.info("completion", "Story passed", {
|
|
52170
52407
|
storyId: completedStory.id,
|
|
@@ -52196,7 +52433,9 @@ var init_completion = __esm(() => {
|
|
|
52196
52433
|
}
|
|
52197
52434
|
}
|
|
52198
52435
|
}
|
|
52199
|
-
|
|
52436
|
+
if (persistPrd) {
|
|
52437
|
+
await _completionDeps.savePRD(ctx.prd, prdPath);
|
|
52438
|
+
}
|
|
52200
52439
|
const updatedCounts = countStories(ctx.prd);
|
|
52201
52440
|
logger.info("completion", "Progress update", {
|
|
52202
52441
|
storyId: ctx.story.id,
|
|
@@ -52981,23 +53220,35 @@ function logUnifiedReviewPhaseStart(storyId, opName) {
|
|
|
52981
53220
|
logger?.info("review", "Running adversarial check", { storyId });
|
|
52982
53221
|
}
|
|
52983
53222
|
}
|
|
52984
|
-
function
|
|
52985
|
-
if (isTddPhase)
|
|
52986
|
-
return;
|
|
52987
|
-
if (opName === "semantic-review" || opName === "adversarial-review")
|
|
52988
|
-
return;
|
|
53223
|
+
function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
|
|
52989
53224
|
if (output === null || output === undefined || typeof output !== "object")
|
|
52990
|
-
return;
|
|
52991
|
-
const logger = getSafeLogger();
|
|
53225
|
+
return null;
|
|
52992
53226
|
const r = output;
|
|
52993
53227
|
const success2 = r.success === true || r.passed === true;
|
|
52994
|
-
const findingsCount = Array.isArray(r.findings) ? r.findings.length : undefined;
|
|
53228
|
+
const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
|
|
52995
53229
|
const status = typeof r.status === "string" ? r.status : undefined;
|
|
52996
53230
|
const data = { storyId, phase: opName, durationMs };
|
|
52997
53231
|
if (findingsCount !== undefined)
|
|
52998
53232
|
data.findingsCount = findingsCount;
|
|
52999
53233
|
if (status !== undefined)
|
|
53000
53234
|
data.status = status;
|
|
53235
|
+
if (typeof r.failureCategory === "string")
|
|
53236
|
+
data.failureCategory = r.failureCategory;
|
|
53237
|
+
if (typeof r.reviewReason === "string")
|
|
53238
|
+
data.reviewReason = r.reviewReason;
|
|
53239
|
+
return { success: success2, data };
|
|
53240
|
+
}
|
|
53241
|
+
function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage, progressData = {}) {
|
|
53242
|
+
if (isTddPhase)
|
|
53243
|
+
return;
|
|
53244
|
+
if (opName === "semantic-review" || opName === "adversarial-review")
|
|
53245
|
+
return;
|
|
53246
|
+
const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
|
|
53247
|
+
if (!built)
|
|
53248
|
+
return;
|
|
53249
|
+
const { success: success2 } = built;
|
|
53250
|
+
const data = { ...built.data, ...progressData };
|
|
53251
|
+
const logger = getSafeLogger();
|
|
53001
53252
|
const message = formatPhaseResultMessage(opName, success2, stage);
|
|
53002
53253
|
if (stage === "rectification") {
|
|
53003
53254
|
logger?.info("story-orchestrator", message, data);
|
|
@@ -53059,17 +53310,21 @@ function logUnifiedReviewPhaseResult(storyId, opName, output) {
|
|
|
53059
53310
|
truncated: findingsCount > findingsSummary.length
|
|
53060
53311
|
});
|
|
53061
53312
|
}
|
|
53062
|
-
async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = false) {
|
|
53313
|
+
async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = false, progress) {
|
|
53063
53314
|
const logger = getSafeLogger();
|
|
53064
53315
|
const opName = slot.op.name;
|
|
53316
|
+
const progressData = progress ? { phaseIndex: progress.index, totalPhases: progress.total } : {};
|
|
53065
53317
|
const isTddPhase = isThreeSession && TDD_OP_NAMES.has(opName);
|
|
53066
53318
|
const beforeRef = isTddPhase ? await _storyOrchestratorDeps.captureGitRef(ctx.packageDir) : undefined;
|
|
53067
53319
|
let dispatchInput = isTddPhase && beforeRef ? { ...slot.input, beforeRef } : slot.input;
|
|
53068
53320
|
dispatchInput = await refreshReviewInputForDispatch(opName, dispatchInput);
|
|
53069
53321
|
if (isTddPhase) {
|
|
53070
|
-
logger?.info("tdd", `-> Session: ${opName}`, { storyId: ctx.storyId, role: opName });
|
|
53322
|
+
logger?.info("tdd", `-> Session: ${opName}`, { storyId: ctx.storyId, role: opName, ...progressData });
|
|
53071
53323
|
} else if (isThreeSession && opName === "full-suite-gate") {
|
|
53072
|
-
logger?.info("tdd", "-> Running full test suite gate (before Verifier)", {
|
|
53324
|
+
logger?.info("tdd", "-> Running full test suite gate (before Verifier)", {
|
|
53325
|
+
storyId: ctx.storyId,
|
|
53326
|
+
...progressData
|
|
53327
|
+
});
|
|
53073
53328
|
}
|
|
53074
53329
|
logUnifiedReviewPhaseStart(ctx.storyId, opName);
|
|
53075
53330
|
const phaseStartedAt = Date.now();
|
|
@@ -53079,7 +53334,7 @@ async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = fa
|
|
|
53079
53334
|
phaseOutputs[opName] = output;
|
|
53080
53335
|
emitReviewDecision(ctx, opName, output);
|
|
53081
53336
|
logUnifiedReviewPhaseResult(ctx.storyId, opName, output);
|
|
53082
|
-
logDeterministicPhaseOutcome(ctx.storyId, opName, output, Date.now() - phaseStartedAt, isTddPhase, slot.op.stage);
|
|
53337
|
+
logDeterministicPhaseOutcome(ctx.storyId, opName, output, Date.now() - phaseStartedAt, isTddPhase, slot.op.stage, progressData);
|
|
53083
53338
|
if (isTddPhase) {
|
|
53084
53339
|
const durationMs = Date.now() - phaseStartedAt;
|
|
53085
53340
|
logger?.info("tdd", `Session complete: ${opName}`, {
|
|
@@ -53248,9 +53503,13 @@ class ExecutionPlan {
|
|
|
53248
53503
|
const phaseOutputs = {};
|
|
53249
53504
|
const startedAt = Date.now();
|
|
53250
53505
|
const logger = getSafeLogger();
|
|
53251
|
-
|
|
53506
|
+
const orderedPhases = collectOrderedPhases(this.state);
|
|
53507
|
+
for (const [phaseIndex, phase] of orderedPhases.entries()) {
|
|
53252
53508
|
try {
|
|
53253
|
-
await runPhase(this.ctx, phase.slot, phaseCosts, phaseOutputs, this.isThreeSession
|
|
53509
|
+
await runPhase(this.ctx, phase.slot, phaseCosts, phaseOutputs, this.isThreeSession, {
|
|
53510
|
+
index: phaseIndex + 1,
|
|
53511
|
+
total: orderedPhases.length
|
|
53512
|
+
});
|
|
53254
53513
|
} catch (error48) {
|
|
53255
53514
|
logger?.error("story-orchestrator", "Phase threw unexpected error", {
|
|
53256
53515
|
storyId: this.ctx.storyId,
|
|
@@ -56915,7 +57174,7 @@ function renderProposals(proposals, runId, observationCount) {
|
|
|
56915
57174
|
}
|
|
56916
57175
|
|
|
56917
57176
|
// src/plugins/builtin/curator/rollup.ts
|
|
56918
|
-
import { appendFile as
|
|
57177
|
+
import { appendFile as appendFile3, mkdir as mkdir10, writeFile } from "fs/promises";
|
|
56919
57178
|
import * as path14 from "path";
|
|
56920
57179
|
async function appendToRollup(observations, rollupPath) {
|
|
56921
57180
|
try {
|
|
@@ -56931,7 +57190,7 @@ async function appendToRollup(observations, rollupPath) {
|
|
|
56931
57190
|
const newLines = `${observations.map((o) => JSON.stringify(o)).join(`
|
|
56932
57191
|
`)}
|
|
56933
57192
|
`;
|
|
56934
|
-
await
|
|
57193
|
+
await appendFile3(rollupPath, newLines);
|
|
56935
57194
|
} catch {}
|
|
56936
57195
|
}
|
|
56937
57196
|
var init_rollup = () => {};
|
|
@@ -57849,7 +58108,7 @@ var package_default;
|
|
|
57849
58108
|
var init_package = __esm(() => {
|
|
57850
58109
|
package_default = {
|
|
57851
58110
|
name: "@nathapp/nax",
|
|
57852
|
-
version: "0.68.
|
|
58111
|
+
version: "0.68.2",
|
|
57853
58112
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
57854
58113
|
type: "module",
|
|
57855
58114
|
bin: {
|
|
@@ -57944,8 +58203,8 @@ var init_version = __esm(() => {
|
|
|
57944
58203
|
NAX_VERSION = package_default.version;
|
|
57945
58204
|
NAX_COMMIT = (() => {
|
|
57946
58205
|
try {
|
|
57947
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
57948
|
-
return "
|
|
58206
|
+
if (/^[0-9a-f]{6,10}$/.test("27a81a5e"))
|
|
58207
|
+
return "27a81a5e";
|
|
57949
58208
|
} catch {}
|
|
57950
58209
|
try {
|
|
57951
58210
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -59361,6 +59620,9 @@ async function handleRunCompletion(options) {
|
|
|
59361
59620
|
if (options.pluginProviderCache) {
|
|
59362
59621
|
await options.pluginProviderCache.disposeAll();
|
|
59363
59622
|
}
|
|
59623
|
+
clearLanguageCache();
|
|
59624
|
+
clearWorkspaceCache();
|
|
59625
|
+
clearGitRootCache();
|
|
59364
59626
|
const finalCounts = countStories(prd);
|
|
59365
59627
|
const fallbackAggregate = deriveRunFallbackAggregates(allStoryMetrics);
|
|
59366
59628
|
pipelineEventBus.emit({
|
|
@@ -59466,7 +59728,10 @@ var init_run_completion = __esm(() => {
|
|
|
59466
59728
|
init_metrics();
|
|
59467
59729
|
init_event_bus();
|
|
59468
59730
|
init_prd();
|
|
59731
|
+
init_detector();
|
|
59469
59732
|
init_scratch_purge();
|
|
59733
|
+
init_workspace();
|
|
59734
|
+
init_smart_runner();
|
|
59470
59735
|
init_run_regression();
|
|
59471
59736
|
_runCompletionDeps = {
|
|
59472
59737
|
runDeferredRegression,
|
|
@@ -59564,7 +59829,7 @@ function precomputeBatchPlan(stories, maxBatchSize = DEFAULT_MAX_BATCH_SIZE) {
|
|
|
59564
59829
|
var DEFAULT_MAX_BATCH_SIZE = 4;
|
|
59565
59830
|
|
|
59566
59831
|
// src/pipeline/subscribers/events-writer.ts
|
|
59567
|
-
import { appendFile as
|
|
59832
|
+
import { appendFile as appendFile4, mkdir as mkdir14 } from "fs/promises";
|
|
59568
59833
|
import { basename as basename14, join as join73 } from "path";
|
|
59569
59834
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
59570
59835
|
const logger = getSafeLogger();
|
|
@@ -59579,7 +59844,7 @@ function wireEventsWriter(bus, feature, runId, workdir) {
|
|
|
59579
59844
|
await mkdir14(eventsDir, { recursive: true });
|
|
59580
59845
|
dirReady = true;
|
|
59581
59846
|
}
|
|
59582
|
-
await
|
|
59847
|
+
await appendFile4(eventsFile, `${JSON.stringify(line)}
|
|
59583
59848
|
`);
|
|
59584
59849
|
} catch (err) {
|
|
59585
59850
|
logger?.warn("events-writer", "Failed to write event line (non-fatal)", {
|
|
@@ -61476,9 +61741,13 @@ var exports_parallel_worker = {};
|
|
|
61476
61741
|
__export(exports_parallel_worker, {
|
|
61477
61742
|
executeStoryInWorktree: () => executeStoryInWorktree,
|
|
61478
61743
|
executeParallelBatch: () => executeParallelBatch,
|
|
61744
|
+
buildWorktreePipelineContext: () => buildWorktreePipelineContext,
|
|
61479
61745
|
_parallelWorkerDeps: () => _parallelWorkerDeps
|
|
61480
61746
|
});
|
|
61481
61747
|
import { join as join79 } from "path";
|
|
61748
|
+
function buildWorktreePipelineContext(base, _story) {
|
|
61749
|
+
return { ...base, prd: structuredClone(base.prd) };
|
|
61750
|
+
}
|
|
61482
61751
|
async function executeStoryInWorktree(story, worktreePath, dependencyContext, context, routing, eventEmitter) {
|
|
61483
61752
|
const logger = getSafeLogger();
|
|
61484
61753
|
try {
|
|
@@ -61492,7 +61761,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
|
|
|
61492
61761
|
}
|
|
61493
61762
|
}
|
|
61494
61763
|
const pipelineContext = {
|
|
61495
|
-
...context,
|
|
61764
|
+
...buildWorktreePipelineContext(context, story),
|
|
61496
61765
|
config: context.config,
|
|
61497
61766
|
rootConfig: context.rootConfig,
|
|
61498
61767
|
story,
|
|
@@ -61792,6 +62061,7 @@ var init_parallel_batch = __esm(() => {
|
|
|
61792
62061
|
// src/execution/unified-executor.ts
|
|
61793
62062
|
var exports_unified_executor = {};
|
|
61794
62063
|
__export(exports_unified_executor, {
|
|
62064
|
+
reconcileBatchOutcome: () => reconcileBatchOutcome,
|
|
61795
62065
|
executeUnified: () => executeUnified,
|
|
61796
62066
|
_unifiedExecutorDeps: () => _unifiedExecutorDeps
|
|
61797
62067
|
});
|
|
@@ -61915,7 +62185,10 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
61915
62185
|
const readyStories = getAllReadyStories(prd);
|
|
61916
62186
|
const batch = _unifiedExecutorDeps.selectIndependentBatch(readyStories, ctx.parallelCount);
|
|
61917
62187
|
if (batch.length > 1) {
|
|
61918
|
-
|
|
62188
|
+
const batchAgent = ctx.agentManager?.getDefault() ?? resolveDefaultAgent(ctx.config);
|
|
62189
|
+
const batchCounts = countStories(prd);
|
|
62190
|
+
const batchBaseDone = batchCounts.total - batchCounts.pending;
|
|
62191
|
+
for (const [batchIndex, story] of batch.entries()) {
|
|
61919
62192
|
const modelTier2 = story.routing?.modelTier ?? ctx.config.autoMode.complexityRouting?.[story.routing?.complexity ?? "medium"] ?? "balanced";
|
|
61920
62193
|
pipelineEventBus.emit({
|
|
61921
62194
|
type: "story:started",
|
|
@@ -61923,7 +62196,7 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
61923
62196
|
story: { id: story.id, title: story.title, status: story.status, attempts: story.attempts },
|
|
61924
62197
|
workdir: ctx.workdir,
|
|
61925
62198
|
modelTier: modelTier2,
|
|
61926
|
-
agent:
|
|
62199
|
+
agent: batchAgent,
|
|
61927
62200
|
iteration: iterations
|
|
61928
62201
|
});
|
|
61929
62202
|
logger?.info("story.start", `${story.title}`, {
|
|
@@ -61931,6 +62204,9 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
61931
62204
|
storyTitle: story.title,
|
|
61932
62205
|
complexity: story.routing?.complexity ?? "unknown",
|
|
61933
62206
|
modelTier: modelTier2,
|
|
62207
|
+
agent: batchAgent,
|
|
62208
|
+
storyNumber: batchBaseDone + batchIndex + 1,
|
|
62209
|
+
storyTotal: batchCounts.total,
|
|
61934
62210
|
attempt: story.attempts + 1
|
|
61935
62211
|
});
|
|
61936
62212
|
}
|
|
@@ -61950,6 +62226,7 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
61950
62226
|
config: ctx.config,
|
|
61951
62227
|
rootConfig: ctx.config,
|
|
61952
62228
|
prd,
|
|
62229
|
+
skipPrdPersistence: true,
|
|
61953
62230
|
projectDir: ctx.workdir,
|
|
61954
62231
|
naxIgnoreIndex,
|
|
61955
62232
|
hooks: ctx.hooks,
|
|
@@ -61997,6 +62274,8 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
61997
62274
|
abortSignal: ctx.abortSignal
|
|
61998
62275
|
}, pipelineResult);
|
|
61999
62276
|
}
|
|
62277
|
+
reconcileBatchOutcome(prd, batchResult);
|
|
62278
|
+
await savePRD(prd, ctx.prdPath);
|
|
62000
62279
|
await pipelineEventBus.drain();
|
|
62001
62280
|
totalCost += batchResult.totalCost;
|
|
62002
62281
|
storiesCompleted += batchResult.completed.length;
|
|
@@ -62087,6 +62366,8 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
62087
62366
|
}
|
|
62088
62367
|
}
|
|
62089
62368
|
const modelTier2 = singleSelection.routing.modelTier;
|
|
62369
|
+
const singleAgent = ctx.agentManager?.getDefault() ?? resolveDefaultAgent(ctx.config);
|
|
62370
|
+
const singleCounts = countStories(prd);
|
|
62090
62371
|
pipelineEventBus.emit({
|
|
62091
62372
|
type: "story:started",
|
|
62092
62373
|
storyId: singleStory.id,
|
|
@@ -62098,7 +62379,7 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
62098
62379
|
},
|
|
62099
62380
|
workdir: ctx.workdir,
|
|
62100
62381
|
modelTier: modelTier2,
|
|
62101
|
-
agent:
|
|
62382
|
+
agent: singleAgent,
|
|
62102
62383
|
iteration: iterations
|
|
62103
62384
|
});
|
|
62104
62385
|
logger?.info("story.start", `${singleStory.title}`, {
|
|
@@ -62106,6 +62387,9 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
62106
62387
|
storyTitle: singleStory.title,
|
|
62107
62388
|
complexity: singleSelection.routing.complexity ?? "unknown",
|
|
62108
62389
|
modelTier: modelTier2,
|
|
62390
|
+
agent: singleAgent,
|
|
62391
|
+
storyNumber: singleCounts.total - singleCounts.pending + 1,
|
|
62392
|
+
storyTotal: singleCounts.total,
|
|
62109
62393
|
attempt: singleStory.attempts + 1
|
|
62110
62394
|
});
|
|
62111
62395
|
const singleIter = await _unifiedExecutorDeps.runIteration(ctx, prd, singleSelection, iterations, totalCost, allStoryMetrics);
|
|
@@ -62157,6 +62441,8 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
62157
62441
|
}
|
|
62158
62442
|
}
|
|
62159
62443
|
const modelTier = selection.routing.modelTier;
|
|
62444
|
+
const seqAgent = ctx.agentManager?.getDefault() ?? resolveDefaultAgent(ctx.config);
|
|
62445
|
+
const seqCounts = countStories(prd);
|
|
62160
62446
|
pipelineEventBus.emit({
|
|
62161
62447
|
type: "story:started",
|
|
62162
62448
|
storyId: selection.story.id,
|
|
@@ -62168,7 +62454,7 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
62168
62454
|
},
|
|
62169
62455
|
workdir: ctx.workdir,
|
|
62170
62456
|
modelTier,
|
|
62171
|
-
agent:
|
|
62457
|
+
agent: seqAgent,
|
|
62172
62458
|
iteration: iterations
|
|
62173
62459
|
});
|
|
62174
62460
|
logger?.info("story.start", `${selection.story.title}`, {
|
|
@@ -62176,6 +62462,9 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
62176
62462
|
storyTitle: selection.story.title,
|
|
62177
62463
|
complexity: selection.routing.complexity ?? "unknown",
|
|
62178
62464
|
modelTier,
|
|
62465
|
+
agent: seqAgent,
|
|
62466
|
+
storyNumber: seqCounts.total - seqCounts.pending + 1,
|
|
62467
|
+
storyTotal: seqCounts.total,
|
|
62179
62468
|
attempt: selection.story.attempts + 1
|
|
62180
62469
|
});
|
|
62181
62470
|
const iter = await _unifiedExecutorDeps.runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics);
|
|
@@ -62234,6 +62523,18 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
62234
62523
|
return buildResult2("max-iterations");
|
|
62235
62524
|
} finally {}
|
|
62236
62525
|
}
|
|
62526
|
+
function reconcileBatchOutcome(prd, batchResult) {
|
|
62527
|
+
for (const story of batchResult.completed) {
|
|
62528
|
+
markStoryPassed(prd, story.id);
|
|
62529
|
+
}
|
|
62530
|
+
for (const conflict of batchResult.mergeConflicts) {
|
|
62531
|
+
if (conflict.rectified) {
|
|
62532
|
+
markStoryPassed(prd, conflict.story.id);
|
|
62533
|
+
} else {
|
|
62534
|
+
markStoryFailed(prd, conflict.story.id, undefined, "merge-conflict");
|
|
62535
|
+
}
|
|
62536
|
+
}
|
|
62537
|
+
}
|
|
62237
62538
|
var TERMINAL_ACTIONS, _unifiedExecutorDeps;
|
|
62238
62539
|
var init_unified_executor = __esm(() => {
|
|
62239
62540
|
init_agents();
|