@nathapp/nax 0.75.6 → 0.76.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nax.js +966 -312
- package/flows/nax-finish/commit-message.ts +136 -0
- package/flows/nax-finish/flow-ctx.ts +95 -0
- package/flows/nax-finish/nax-finish.flow.ts +129 -30
- package/flows/nax-finish/review-prompts.ts +42 -4
- package/flows/nax-finish/steps/context.ts +38 -0
- package/flows/nax-finish/steps/git.ts +26 -3
- package/flows/nax-finish/steps/result.ts +99 -10
- package/flows/nax-finish/types.ts +42 -0
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -17823,6 +17823,7 @@ var init_schemas3 = __esm(() => {
|
|
|
17823
17823
|
enabled: exports_external.boolean().default(false),
|
|
17824
17824
|
flowPath: exports_external.string().default("flows/nax-finish/nax-finish.flow.ts"),
|
|
17825
17825
|
defaultAgent: exports_external.string().nullable().default(null),
|
|
17826
|
+
model: exports_external.string().min(1, "model must be non-empty").nullable().default(null),
|
|
17826
17827
|
reviewers: exports_external.object({
|
|
17827
17828
|
spec: exports_external.string().nullable().default(null),
|
|
17828
17829
|
quality: exports_external.string().nullable().default(null)
|
|
@@ -17839,6 +17840,7 @@ var init_schemas3 = __esm(() => {
|
|
|
17839
17840
|
enabled: false,
|
|
17840
17841
|
flowPath: "flows/nax-finish/nax-finish.flow.ts",
|
|
17841
17842
|
defaultAgent: null,
|
|
17843
|
+
model: null,
|
|
17842
17844
|
reviewers: { spec: null, quality: null },
|
|
17843
17845
|
escalate: { telegram: true },
|
|
17844
17846
|
notify: { mode: "escalation" },
|
|
@@ -17849,6 +17851,7 @@ var init_schemas3 = __esm(() => {
|
|
|
17849
17851
|
enabled: false,
|
|
17850
17852
|
flowPath: "flows/nax-finish/nax-finish.flow.ts",
|
|
17851
17853
|
defaultAgent: null,
|
|
17854
|
+
model: null,
|
|
17852
17855
|
reviewers: { spec: null, quality: null },
|
|
17853
17856
|
escalate: { telegram: true },
|
|
17854
17857
|
notify: { mode: "escalation" },
|
|
@@ -19639,6 +19642,13 @@ function reshapeSelector(name, fn) {
|
|
|
19639
19642
|
return { name, select: fn };
|
|
19640
19643
|
}
|
|
19641
19644
|
|
|
19645
|
+
// src/config/project-key.ts
|
|
19646
|
+
import { basename as basename3 } from "path";
|
|
19647
|
+
function getProjectKey(config2, projectDir) {
|
|
19648
|
+
return config2.name?.trim() || basename3(projectDir);
|
|
19649
|
+
}
|
|
19650
|
+
var init_project_key = () => {};
|
|
19651
|
+
|
|
19642
19652
|
// src/config/selectors.ts
|
|
19643
19653
|
var reviewConfigSelector, planConfigSelector, decomposeConfigSelector, rectifyConfigSelector, acceptanceConfigSelector, acceptanceFixConfigSelector, acceptanceGenConfigSelector, tddConfigSelector, debateConfigSelector, routingConfigSelector, verifyConfigSelector, mutationCheckConfigSelector, rectificationGateConfigSelector, agentConfigSelector, agentManagerConfigSelector, interactionConfigSelector, precheckConfigSelector, qualityConfigSelector, autofixConfigSelector, executionGatesConfigSelector, testPatternConfigSelector, contextConfigSelector, contextToolRuntimeConfigSelector, promptLoaderConfigSelector, llmRoutingConfigSelector, finishConfigSelector;
|
|
19644
19654
|
var init_selectors = __esm(() => {
|
|
@@ -19659,7 +19669,7 @@ var init_selectors = __esm(() => {
|
|
|
19659
19669
|
mutationCheckConfigSelector = reshapeSelector("mutation-check", (c) => c.execution?.mutationCheck);
|
|
19660
19670
|
rectificationGateConfigSelector = pickSelector("rectification-gate", "execution", "models", "agent", "quality", "review");
|
|
19661
19671
|
agentConfigSelector = pickSelector("agent", "agent");
|
|
19662
|
-
agentManagerConfigSelector = pickSelector("agent-manager", "agent", "execution");
|
|
19672
|
+
agentManagerConfigSelector = pickSelector("agent-manager", "agent", "execution", "profile");
|
|
19663
19673
|
interactionConfigSelector = pickSelector("interaction", "interaction");
|
|
19664
19674
|
precheckConfigSelector = pickSelector("precheck", "precheck", "quality", "execution", "prompts", "review", "project");
|
|
19665
19675
|
qualityConfigSelector = pickSelector("quality", "quality", "execution");
|
|
@@ -19670,7 +19680,7 @@ var init_selectors = __esm(() => {
|
|
|
19670
19680
|
contextToolRuntimeConfigSelector = pickSelector("context-tool-runtime", "context", "execution", "project", "quality");
|
|
19671
19681
|
promptLoaderConfigSelector = pickSelector("prompt-loader", "prompts", "context", "project");
|
|
19672
19682
|
llmRoutingConfigSelector = pickSelector("llm-routing", "routing", "models", "agent", "tdd", "execution", "precheck");
|
|
19673
|
-
finishConfigSelector = pickSelector("finish", "finish", "interaction", "quality");
|
|
19683
|
+
finishConfigSelector = pickSelector("finish", "finish", "interaction", "quality", "agent");
|
|
19674
19684
|
});
|
|
19675
19685
|
|
|
19676
19686
|
// src/config/loader-runtime.ts
|
|
@@ -19950,6 +19960,7 @@ __export(exports_config, {
|
|
|
19950
19960
|
interactionConfigSelector: () => interactionConfigSelector,
|
|
19951
19961
|
globalConfigPath: () => globalConfigPath,
|
|
19952
19962
|
globalConfigDir: () => globalConfigDir,
|
|
19963
|
+
getProjectKey: () => getProjectKey,
|
|
19953
19964
|
getAcQualityRules: () => getAcQualityRules,
|
|
19954
19965
|
finishConfigSelector: () => finishConfigSelector,
|
|
19955
19966
|
findProjectDir: () => findProjectDir,
|
|
@@ -20001,6 +20012,7 @@ var init_config = __esm(() => {
|
|
|
20001
20012
|
init_path_security();
|
|
20002
20013
|
init_paths();
|
|
20003
20014
|
init_profile();
|
|
20015
|
+
init_project_key();
|
|
20004
20016
|
init_selectors();
|
|
20005
20017
|
init_test_strategy();
|
|
20006
20018
|
});
|
|
@@ -20113,6 +20125,11 @@ function estimateCostFromTokenUsage(usage, model) {
|
|
|
20113
20125
|
const cacheCreationCost = (usage.cacheCreationInputTokens ?? 0) * cacheCreationRate;
|
|
20114
20126
|
return inputCost + outputCost + cacheReadCost + cacheCreationCost;
|
|
20115
20127
|
}
|
|
20128
|
+
function resolvePricingSource(model) {
|
|
20129
|
+
if (model === undefined || model === "" || model === "unknown")
|
|
20130
|
+
return "unknown-model";
|
|
20131
|
+
return MODEL_PRICING[model] ? "model-rates" : "fallback-rates";
|
|
20132
|
+
}
|
|
20116
20133
|
var init_calculate = __esm(() => {
|
|
20117
20134
|
init_pricing();
|
|
20118
20135
|
});
|
|
@@ -21116,6 +21133,8 @@ class AcpSessionHandleImpl {
|
|
|
21116
21133
|
id;
|
|
21117
21134
|
agentName;
|
|
21118
21135
|
protocolIds;
|
|
21136
|
+
modelDef;
|
|
21137
|
+
modelTier;
|
|
21119
21138
|
_client;
|
|
21120
21139
|
_session;
|
|
21121
21140
|
_sessionName;
|
|
@@ -21133,6 +21152,8 @@ class AcpSessionHandleImpl {
|
|
|
21133
21152
|
this._resumed = opts.resumed;
|
|
21134
21153
|
this._timeoutSeconds = opts.timeoutSeconds;
|
|
21135
21154
|
this._modelDef = opts.modelDef;
|
|
21155
|
+
this.modelDef = opts.modelDef;
|
|
21156
|
+
this.modelTier = opts.modelTier;
|
|
21136
21157
|
this._permissionMode = opts.permissionMode;
|
|
21137
21158
|
}
|
|
21138
21159
|
}
|
|
@@ -21518,6 +21539,7 @@ class AcpAgentAdapter {
|
|
|
21518
21539
|
resumed: ensured.resumed,
|
|
21519
21540
|
timeoutSeconds,
|
|
21520
21541
|
modelDef,
|
|
21542
|
+
modelTier: opts.modelTier,
|
|
21521
21543
|
permissionMode: resolvedPermissions.mode
|
|
21522
21544
|
});
|
|
21523
21545
|
} catch (error48) {
|
|
@@ -22113,6 +22135,92 @@ function formatSessionName(req) {
|
|
|
22113
22135
|
}
|
|
22114
22136
|
var init_session_name = () => {};
|
|
22115
22137
|
|
|
22138
|
+
// src/agents/manager-dispatch.ts
|
|
22139
|
+
function modelAttribution(src) {
|
|
22140
|
+
return {
|
|
22141
|
+
...src.modelDef?.model !== undefined ? { model: src.modelDef.model } : {},
|
|
22142
|
+
...src.modelTier !== undefined ? { modelTier: src.modelTier } : {}
|
|
22143
|
+
};
|
|
22144
|
+
}
|
|
22145
|
+
function buildSessionTurnEvent(input) {
|
|
22146
|
+
const { handle, result, opts, startedAt } = input;
|
|
22147
|
+
return {
|
|
22148
|
+
kind: "session-turn",
|
|
22149
|
+
sessionName: handle.id,
|
|
22150
|
+
sessionRole: input.sessionRole,
|
|
22151
|
+
prompt: input.prompt,
|
|
22152
|
+
response: result.output,
|
|
22153
|
+
agentName: input.agentName,
|
|
22154
|
+
...modelAttribution(handle),
|
|
22155
|
+
...input.profile !== undefined ? { profile: input.profile } : {},
|
|
22156
|
+
stage: input.stage,
|
|
22157
|
+
storyId: opts.storyId,
|
|
22158
|
+
featureName: opts.featureName,
|
|
22159
|
+
workdir: opts.workdir,
|
|
22160
|
+
projectDir: opts.projectDir,
|
|
22161
|
+
resolvedPermissions: input.resolvedPermissions,
|
|
22162
|
+
tokenUsage: result.tokenUsage,
|
|
22163
|
+
estimatedCostUsd: result.estimatedCostUsd,
|
|
22164
|
+
exactCostUsd: result.exactCostUsd,
|
|
22165
|
+
durationMs: Date.now() - startedAt,
|
|
22166
|
+
timestamp: Date.now(),
|
|
22167
|
+
turn: result.internalRoundTrips ?? 1,
|
|
22168
|
+
protocolIds: {
|
|
22169
|
+
sessionId: handle.protocolIds?.sessionId ?? null,
|
|
22170
|
+
recordId: handle.protocolIds?.recordId ?? null
|
|
22171
|
+
},
|
|
22172
|
+
...result.interactions?.length ? { interactions: result.interactions } : {},
|
|
22173
|
+
origin: "runAsSession",
|
|
22174
|
+
...opts.callId !== undefined ? { callId: opts.callId } : {},
|
|
22175
|
+
...opts.scopeId !== undefined ? { scopeId: opts.scopeId } : {}
|
|
22176
|
+
};
|
|
22177
|
+
}
|
|
22178
|
+
function buildCompleteEvent(input) {
|
|
22179
|
+
const { options } = input;
|
|
22180
|
+
return {
|
|
22181
|
+
kind: "complete",
|
|
22182
|
+
sessionName: input.sessionName,
|
|
22183
|
+
sessionRole: options.sessionRole ?? "auto",
|
|
22184
|
+
prompt: input.prompt,
|
|
22185
|
+
response: input.response,
|
|
22186
|
+
agentName: input.agentName,
|
|
22187
|
+
...modelAttribution(options),
|
|
22188
|
+
...input.profile !== undefined ? { profile: input.profile } : {},
|
|
22189
|
+
stage: input.stage,
|
|
22190
|
+
storyId: options.storyId,
|
|
22191
|
+
featureName: options.featureName,
|
|
22192
|
+
workdir: options.workdir,
|
|
22193
|
+
resolvedPermissions: input.resolvedPermissions,
|
|
22194
|
+
tokenUsage: input.tokenUsage,
|
|
22195
|
+
estimatedCostUsd: input.estimatedCostUsd,
|
|
22196
|
+
exactCostUsd: input.exactCostUsd,
|
|
22197
|
+
durationMs: Date.now() - input.startedAt,
|
|
22198
|
+
timestamp: Date.now(),
|
|
22199
|
+
...options.callId !== undefined ? { callId: options.callId } : {},
|
|
22200
|
+
...options.scopeId !== undefined ? { scopeId: options.scopeId } : {}
|
|
22201
|
+
};
|
|
22202
|
+
}
|
|
22203
|
+
function buildDispatchErrorEvent(input) {
|
|
22204
|
+
return {
|
|
22205
|
+
kind: "error",
|
|
22206
|
+
origin: input.origin,
|
|
22207
|
+
agentName: input.agentName,
|
|
22208
|
+
stage: input.stage,
|
|
22209
|
+
storyId: input.storyId,
|
|
22210
|
+
errorCode: input.error instanceof NaxError ? input.error.code : "DISPATCH_ERROR",
|
|
22211
|
+
errorMessage: errorMessage(input.error),
|
|
22212
|
+
prompt: input.prompt,
|
|
22213
|
+
durationMs: Date.now() - input.startedAt,
|
|
22214
|
+
timestamp: Date.now(),
|
|
22215
|
+
resolvedPermissions: input.resolvedPermissions,
|
|
22216
|
+
...input.callId !== undefined ? { callId: input.callId } : {},
|
|
22217
|
+
...input.scopeId !== undefined ? { scopeId: input.scopeId } : {}
|
|
22218
|
+
};
|
|
22219
|
+
}
|
|
22220
|
+
var init_manager_dispatch = __esm(() => {
|
|
22221
|
+
init_errors();
|
|
22222
|
+
});
|
|
22223
|
+
|
|
22116
22224
|
// src/agents/retry/default-strategy.ts
|
|
22117
22225
|
var MAX_RETRIES = 3, defaultRetryStrategy;
|
|
22118
22226
|
var init_default_strategy = __esm(() => {
|
|
@@ -22740,52 +22848,33 @@ class AgentManager {
|
|
|
22740
22848
|
...rawResult,
|
|
22741
22849
|
protocolIds: rawResult.protocolIds ?? handle.protocolIds
|
|
22742
22850
|
};
|
|
22743
|
-
const event = {
|
|
22744
|
-
|
|
22745
|
-
sessionName: handle.id,
|
|
22851
|
+
const event = buildSessionTurnEvent({
|
|
22852
|
+
handle,
|
|
22746
22853
|
sessionRole,
|
|
22747
22854
|
prompt,
|
|
22748
|
-
|
|
22855
|
+
result,
|
|
22749
22856
|
agentName,
|
|
22750
22857
|
stage,
|
|
22751
|
-
|
|
22752
|
-
featureName: opts.featureName,
|
|
22753
|
-
workdir: opts.workdir,
|
|
22754
|
-
projectDir: opts.projectDir,
|
|
22858
|
+
opts,
|
|
22755
22859
|
resolvedPermissions,
|
|
22756
|
-
|
|
22757
|
-
|
|
22758
|
-
|
|
22759
|
-
durationMs: Date.now() - start,
|
|
22760
|
-
timestamp: Date.now(),
|
|
22761
|
-
turn: result.internalRoundTrips ?? 1,
|
|
22762
|
-
protocolIds: {
|
|
22763
|
-
sessionId: handle.protocolIds?.sessionId ?? null,
|
|
22764
|
-
recordId: handle.protocolIds?.recordId ?? null
|
|
22765
|
-
},
|
|
22766
|
-
...result.interactions?.length ? { interactions: result.interactions } : {},
|
|
22767
|
-
origin: "runAsSession",
|
|
22768
|
-
...opts.callId !== undefined ? { callId: opts.callId } : {},
|
|
22769
|
-
...opts.scopeId !== undefined ? { scopeId: opts.scopeId } : {}
|
|
22770
|
-
};
|
|
22860
|
+
profile: this._config.profile,
|
|
22861
|
+
startedAt: start
|
|
22862
|
+
});
|
|
22771
22863
|
this._dispatchEvents.emitDispatch(event);
|
|
22772
22864
|
return result;
|
|
22773
22865
|
} catch (err) {
|
|
22774
|
-
const errEvent = {
|
|
22775
|
-
kind: "error",
|
|
22866
|
+
const errEvent = buildDispatchErrorEvent({
|
|
22776
22867
|
origin: "runAsSession",
|
|
22777
22868
|
agentName,
|
|
22778
22869
|
stage,
|
|
22779
22870
|
storyId: opts.storyId,
|
|
22780
|
-
|
|
22781
|
-
errorMessage: errorMessage(err),
|
|
22871
|
+
error: err,
|
|
22782
22872
|
prompt,
|
|
22783
|
-
durationMs: Date.now() - start,
|
|
22784
|
-
timestamp: Date.now(),
|
|
22785
22873
|
resolvedPermissions,
|
|
22786
|
-
|
|
22787
|
-
|
|
22788
|
-
|
|
22874
|
+
callId: opts.callId,
|
|
22875
|
+
scopeId: opts.scopeId,
|
|
22876
|
+
startedAt: start
|
|
22877
|
+
});
|
|
22789
22878
|
this._dispatchEvents.emitDispatchError(errEvent);
|
|
22790
22879
|
throw err;
|
|
22791
22880
|
}
|
|
@@ -22804,44 +22893,35 @@ class AgentManager {
|
|
|
22804
22893
|
const start = Date.now();
|
|
22805
22894
|
try {
|
|
22806
22895
|
const outcome = await this.completeWithFallback(prompt, augmented, agentName);
|
|
22807
|
-
const event = {
|
|
22808
|
-
kind: "complete",
|
|
22896
|
+
const event = buildCompleteEvent({
|
|
22809
22897
|
sessionName,
|
|
22810
|
-
sessionRole: options.sessionRole ?? "auto",
|
|
22811
22898
|
prompt,
|
|
22812
22899
|
response: outcome.result.output,
|
|
22813
22900
|
agentName,
|
|
22814
22901
|
stage,
|
|
22815
|
-
|
|
22816
|
-
featureName: options.featureName,
|
|
22817
|
-
workdir: options.workdir,
|
|
22902
|
+
options,
|
|
22818
22903
|
resolvedPermissions,
|
|
22819
22904
|
tokenUsage: outcome.result.tokenUsage,
|
|
22820
22905
|
estimatedCostUsd: outcome.result.estimatedCostUsd,
|
|
22821
22906
|
exactCostUsd: outcome.result.exactCostUsd,
|
|
22822
|
-
|
|
22823
|
-
|
|
22824
|
-
|
|
22825
|
-
...options.scopeId !== undefined ? { scopeId: options.scopeId } : {}
|
|
22826
|
-
};
|
|
22907
|
+
profile: this._config.profile,
|
|
22908
|
+
startedAt: start
|
|
22909
|
+
});
|
|
22827
22910
|
this._dispatchEvents.emitDispatch(event);
|
|
22828
22911
|
return outcome.result;
|
|
22829
22912
|
} catch (err) {
|
|
22830
|
-
const errEvent = {
|
|
22831
|
-
kind: "error",
|
|
22913
|
+
const errEvent = buildDispatchErrorEvent({
|
|
22832
22914
|
origin: "completeAs",
|
|
22833
22915
|
agentName,
|
|
22834
22916
|
stage,
|
|
22835
22917
|
storyId: options.storyId,
|
|
22836
|
-
|
|
22837
|
-
errorMessage: errorMessage(err),
|
|
22918
|
+
error: err,
|
|
22838
22919
|
prompt,
|
|
22839
|
-
durationMs: Date.now() - start,
|
|
22840
|
-
timestamp: Date.now(),
|
|
22841
22920
|
resolvedPermissions,
|
|
22842
|
-
|
|
22843
|
-
|
|
22844
|
-
|
|
22921
|
+
callId: options.callId,
|
|
22922
|
+
scopeId: options.scopeId,
|
|
22923
|
+
startedAt: start
|
|
22924
|
+
});
|
|
22845
22925
|
this._dispatchEvents.emitDispatchError(errEvent);
|
|
22846
22926
|
throw err;
|
|
22847
22927
|
}
|
|
@@ -22864,6 +22944,7 @@ var init_manager = __esm(() => {
|
|
|
22864
22944
|
init_dispatch_events();
|
|
22865
22945
|
init_session_name();
|
|
22866
22946
|
init_bun_deps();
|
|
22947
|
+
init_manager_dispatch();
|
|
22867
22948
|
init_registry();
|
|
22868
22949
|
init_default_strategy();
|
|
22869
22950
|
init_hop_retry_policy();
|
|
@@ -23553,6 +23634,56 @@ var init_digest = __esm(() => {
|
|
|
23553
23634
|
SCOPE_ORDER2 = ["project", "feature", "story", "session", "retrieved"];
|
|
23554
23635
|
});
|
|
23555
23636
|
|
|
23637
|
+
// src/context/engine/manifest-builder.ts
|
|
23638
|
+
function buildManifest(inputs) {
|
|
23639
|
+
const {
|
|
23640
|
+
requestId,
|
|
23641
|
+
request,
|
|
23642
|
+
packed,
|
|
23643
|
+
usedTokens,
|
|
23644
|
+
digestTokens: digestTokens2,
|
|
23645
|
+
buildMs,
|
|
23646
|
+
providerResults,
|
|
23647
|
+
roleFiltered,
|
|
23648
|
+
belowMin,
|
|
23649
|
+
dedupeDropped,
|
|
23650
|
+
budgetExcludedIds,
|
|
23651
|
+
floorPackedIds,
|
|
23652
|
+
floorOverageIds
|
|
23653
|
+
} = inputs;
|
|
23654
|
+
const staleChunkIds = packed.filter((c) => c.staleCandidate).map((c) => c.id);
|
|
23655
|
+
const chunkSummaries = {};
|
|
23656
|
+
const chunkTokens = {};
|
|
23657
|
+
for (const c of packed) {
|
|
23658
|
+
chunkSummaries[c.id] = c.content.slice(0, CHUNK_SUMMARY_CHARS);
|
|
23659
|
+
chunkTokens[c.id] = c.tokens;
|
|
23660
|
+
}
|
|
23661
|
+
return {
|
|
23662
|
+
requestId,
|
|
23663
|
+
stage: request.stage,
|
|
23664
|
+
totalBudgetTokens: request.budgetTokens,
|
|
23665
|
+
usedTokens: usedTokens + digestTokens2,
|
|
23666
|
+
includedChunks: packed.map((c) => c.id),
|
|
23667
|
+
excludedChunks: [
|
|
23668
|
+
...roleFiltered.map((c) => ({ id: c.id, reason: "role-filter" })),
|
|
23669
|
+
...belowMin.map((c) => ({ id: c.id, reason: "below-min-score" })),
|
|
23670
|
+
...dedupeDropped.map((id) => ({ id, reason: "dedupe" })),
|
|
23671
|
+
...budgetExcludedIds.map((id) => ({ id, reason: "budget" }))
|
|
23672
|
+
],
|
|
23673
|
+
floorItems: floorPackedIds,
|
|
23674
|
+
floorOverageItems: floorOverageIds.length > 0 ? floorOverageIds : undefined,
|
|
23675
|
+
digestTokens: digestTokens2,
|
|
23676
|
+
buildMs,
|
|
23677
|
+
providerResults,
|
|
23678
|
+
repoRoot: request.repoRoot,
|
|
23679
|
+
packageDir: request.packageDir,
|
|
23680
|
+
...Object.keys(chunkSummaries).length > 0 && { chunkSummaries },
|
|
23681
|
+
...Object.keys(chunkTokens).length > 0 && { chunkTokens },
|
|
23682
|
+
...staleChunkIds.length > 0 && { staleChunks: staleChunkIds }
|
|
23683
|
+
};
|
|
23684
|
+
}
|
|
23685
|
+
var CHUNK_SUMMARY_CHARS = 300;
|
|
23686
|
+
|
|
23556
23687
|
// src/context/engine/packing.ts
|
|
23557
23688
|
function packChunks(chunks, budgetTokens, availableBudgetTokens) {
|
|
23558
23689
|
const effectiveBudget = availableBudgetTokens !== undefined ? Math.min(budgetTokens, availableBudgetTokens) : budgetTokens;
|
|
@@ -25773,7 +25904,7 @@ async function captureWorkingTreeChanges(workdir, baseRef, scopePrefix) {
|
|
|
25773
25904
|
return [];
|
|
25774
25905
|
const runDiff = async (args) => {
|
|
25775
25906
|
const fullArgs = scopePrefix ? [...args, "--", `${scopePrefix}/`] : args;
|
|
25776
|
-
const { stdout, exitCode } = await gitWithTimeout(fullArgs, workdir,
|
|
25907
|
+
const { stdout, exitCode } = await gitWithTimeout(fullArgs, workdir, _gitDeps.timeoutRetryGitTimeoutMs);
|
|
25777
25908
|
if (exitCode !== 0)
|
|
25778
25909
|
return [];
|
|
25779
25910
|
return stdout.trim().split(`
|
|
@@ -25822,16 +25953,20 @@ async function captureDiffSummary(workdir, baseRef, scopePrefix) {
|
|
|
25822
25953
|
return "";
|
|
25823
25954
|
}
|
|
25824
25955
|
}
|
|
25825
|
-
var
|
|
25956
|
+
var GIT_TIMEOUT_MS = 1e4, TIMEOUT_RETRY_GIT_TIMEOUT_MS = 3000, _gitDeps;
|
|
25826
25957
|
var init_git = __esm(() => {
|
|
25827
25958
|
init_logger2();
|
|
25828
25959
|
init_bun_deps();
|
|
25829
|
-
_gitDeps = {
|
|
25960
|
+
_gitDeps = {
|
|
25961
|
+
spawn,
|
|
25962
|
+
getSafeLogger,
|
|
25963
|
+
timeoutRetryGitTimeoutMs: TIMEOUT_RETRY_GIT_TIMEOUT_MS
|
|
25964
|
+
};
|
|
25830
25965
|
});
|
|
25831
25966
|
|
|
25832
25967
|
// src/utils/path-filters.ts
|
|
25833
25968
|
import { join as join8, relative as relative2 } from "path";
|
|
25834
|
-
function
|
|
25969
|
+
function basename4(path) {
|
|
25835
25970
|
const stripped = path.startsWith("./") ? path.slice(2) : path;
|
|
25836
25971
|
const idx = stripped.lastIndexOf("/");
|
|
25837
25972
|
return idx === -1 ? stripped : stripped.slice(idx + 1);
|
|
@@ -25961,7 +26096,7 @@ function isNaxInternalPath(path) {
|
|
|
25961
26096
|
return true;
|
|
25962
26097
|
if (path.includes("/.nax/"))
|
|
25963
26098
|
return true;
|
|
25964
|
-
return LOCKFILE_BASENAMES.has(
|
|
26099
|
+
return LOCKFILE_BASENAMES.has(basename4(path));
|
|
25965
26100
|
}
|
|
25966
26101
|
function filterNaxInternalPaths(paths, ignoreMatchers = []) {
|
|
25967
26102
|
return paths.filter((path) => !isNaxInternalPath(path) && !ignoreMatchers.some((matcher) => matcher.test(path)));
|
|
@@ -26005,8 +26140,8 @@ async function getGitRootMemo(workdir) {
|
|
|
26005
26140
|
return result ?? null;
|
|
26006
26141
|
}
|
|
26007
26142
|
function extractBasenamePattern(pattern) {
|
|
26008
|
-
const
|
|
26009
|
-
const parts =
|
|
26143
|
+
const basename5 = pattern.slice(pattern.lastIndexOf("/") + 1);
|
|
26144
|
+
const parts = basename5.split("*");
|
|
26010
26145
|
if (parts.length !== 2)
|
|
26011
26146
|
return null;
|
|
26012
26147
|
const [prefix, suffix] = parts;
|
|
@@ -26018,8 +26153,8 @@ function extractSearchTerms(sourceFile) {
|
|
|
26018
26153
|
const withoutPrefix = sourceFile.replace(/^(?:.*\/)?src\//, "");
|
|
26019
26154
|
const withoutExt = withoutPrefix.replace(/\.[^.]+$/, "");
|
|
26020
26155
|
const parts = withoutExt.split("/");
|
|
26021
|
-
const
|
|
26022
|
-
return [`/${
|
|
26156
|
+
const basename5 = parts[parts.length - 1];
|
|
26157
|
+
return [`/${basename5}`, withoutExt];
|
|
26023
26158
|
}
|
|
26024
26159
|
async function importGrepFallback(sourceFiles, workdir, testFilePatterns, maxScanFiles = MAX_GREP_TEST_FILES) {
|
|
26025
26160
|
if (sourceFiles.length === 0 || testFilePatterns.length === 0)
|
|
@@ -27839,33 +27974,21 @@ class ContextOrchestrator {
|
|
|
27839
27974
|
const digest = buildDigest(packed);
|
|
27840
27975
|
const dTokens = digestTokens(digest);
|
|
27841
27976
|
const buildMs = _orchestratorDeps.now() - startMs;
|
|
27842
|
-
const
|
|
27843
|
-
const chunkSummaries = {};
|
|
27844
|
-
for (const c of packed) {
|
|
27845
|
-
chunkSummaries[c.id] = c.content.slice(0, 300);
|
|
27846
|
-
}
|
|
27847
|
-
const manifest = {
|
|
27977
|
+
const manifest = buildManifest({
|
|
27848
27978
|
requestId,
|
|
27849
|
-
|
|
27850
|
-
|
|
27851
|
-
usedTokens
|
|
27852
|
-
includedChunks: packed.map((c) => c.id),
|
|
27853
|
-
excludedChunks: [
|
|
27854
|
-
...roleFiltered.map((c) => ({ id: c.id, reason: "role-filter" })),
|
|
27855
|
-
...belowMin.map((c) => ({ id: c.id, reason: "below-min-score" })),
|
|
27856
|
-
...dedupeDropped.map((id) => ({ id, reason: "dedupe" })),
|
|
27857
|
-
...budgetExcludedIds.map((id) => ({ id, reason: "budget" }))
|
|
27858
|
-
],
|
|
27859
|
-
floorItems: floorPackedIds,
|
|
27860
|
-
floorOverageItems: floorOverageIds.length > 0 ? floorOverageIds : undefined,
|
|
27979
|
+
request,
|
|
27980
|
+
packed,
|
|
27981
|
+
usedTokens,
|
|
27861
27982
|
digestTokens: dTokens,
|
|
27862
27983
|
buildMs,
|
|
27863
27984
|
providerResults,
|
|
27864
|
-
|
|
27865
|
-
|
|
27866
|
-
|
|
27867
|
-
|
|
27868
|
-
|
|
27985
|
+
roleFiltered,
|
|
27986
|
+
belowMin,
|
|
27987
|
+
dedupeDropped,
|
|
27988
|
+
budgetExcludedIds,
|
|
27989
|
+
floorPackedIds,
|
|
27990
|
+
floorOverageIds
|
|
27991
|
+
});
|
|
27869
27992
|
logger.debug("context-v2", "Bundle assembled", {
|
|
27870
27993
|
storyId: request.storyId,
|
|
27871
27994
|
stage: request.stage,
|
|
@@ -27922,6 +28045,7 @@ class ContextOrchestrator {
|
|
|
27922
28045
|
...prior.manifest,
|
|
27923
28046
|
requestId: _orchestratorDeps.uuid(),
|
|
27924
28047
|
includedChunks: packedChunks.map((c) => c.id),
|
|
28048
|
+
chunkTokens: Object.fromEntries(packedChunks.map((c) => [c.id, c.tokens])),
|
|
27925
28049
|
usedTokens: Math.max(0, prior.manifest.usedTokens - prior.manifest.digestTokens + dTokens + extraTokens),
|
|
27926
28050
|
digestTokens: dTokens,
|
|
27927
28051
|
buildMs: 0,
|
|
@@ -27959,7 +28083,7 @@ var init_orchestrator = __esm(() => {
|
|
|
27959
28083
|
});
|
|
27960
28084
|
|
|
27961
28085
|
// src/context/rules/canonical-loader.ts
|
|
27962
|
-
import { basename as
|
|
28086
|
+
import { basename as basename5, join as join12 } from "path";
|
|
27963
28087
|
function parseRuleAllowMarker(line) {
|
|
27964
28088
|
const allowed = new Set;
|
|
27965
28089
|
RULE_ALLOW_MARKER.lastIndex = 0;
|
|
@@ -28099,7 +28223,7 @@ async function loadCanonicalRules(workdir, options = {}) {
|
|
|
28099
28223
|
const filePaths = allFilePaths.filter((filePath) => {
|
|
28100
28224
|
const normalized = filePath.replaceAll("\\", "/");
|
|
28101
28225
|
const normalizedRulesDir = rulesDir.replaceAll("\\", "/");
|
|
28102
|
-
const relativePath = normalized.startsWith(`${normalizedRulesDir}/`) ? normalized.slice(normalizedRulesDir.length + 1) :
|
|
28226
|
+
const relativePath = normalized.startsWith(`${normalizedRulesDir}/`) ? normalized.slice(normalizedRulesDir.length + 1) : basename5(normalized);
|
|
28103
28227
|
return relativePath.split("/").length <= 2;
|
|
28104
28228
|
});
|
|
28105
28229
|
if (allFilePaths.length > filePaths.length) {
|
|
@@ -28115,8 +28239,8 @@ async function loadCanonicalRules(workdir, options = {}) {
|
|
|
28115
28239
|
for (const filePath of filePaths) {
|
|
28116
28240
|
const normalizedPath = filePath.replaceAll("\\", "/");
|
|
28117
28241
|
const normalizedRulesDir = rulesDir.replaceAll("\\", "/");
|
|
28118
|
-
const relativePath = normalizedPath.startsWith(`${normalizedRulesDir}/`) ? normalizedPath.slice(normalizedRulesDir.length + 1) :
|
|
28119
|
-
const fileName =
|
|
28242
|
+
const relativePath = normalizedPath.startsWith(`${normalizedRulesDir}/`) ? normalizedPath.slice(normalizedRulesDir.length + 1) : basename5(normalizedPath);
|
|
28243
|
+
const fileName = basename5(filePath);
|
|
28120
28244
|
let content;
|
|
28121
28245
|
try {
|
|
28122
28246
|
content = await _canonicalLoaderDeps.readFile(filePath);
|
|
@@ -29778,8 +29902,8 @@ function deriveTestPatterns(contextFiles, resolvedGlobs) {
|
|
|
29778
29902
|
const suffixes = resolvedGlobs ? extractGlobSuffixes(resolvedGlobs) : DEFAULT_TS_DERIVE_SUFFIXES;
|
|
29779
29903
|
const effectiveSuffixes = suffixes.length > 0 ? suffixes : DEFAULT_TS_DERIVE_SUFFIXES;
|
|
29780
29904
|
for (const filePath of contextFiles) {
|
|
29781
|
-
const
|
|
29782
|
-
const basenameNoExt =
|
|
29905
|
+
const basename6 = path.basename(filePath);
|
|
29906
|
+
const basenameNoExt = basename6.replace(/\.[^.]+$/, "");
|
|
29783
29907
|
for (const suffix of effectiveSuffixes) {
|
|
29784
29908
|
patterns.add(`${basenameNoExt}${suffix}`);
|
|
29785
29909
|
}
|
|
@@ -29841,8 +29965,8 @@ async function scanTestFiles(options) {
|
|
|
29841
29965
|
const files = [];
|
|
29842
29966
|
for await (const filePath of glob.scan({ cwd: scanDir, absolute: false })) {
|
|
29843
29967
|
if (allowedBasenames !== null) {
|
|
29844
|
-
const
|
|
29845
|
-
if (!allowedBasenames.has(
|
|
29968
|
+
const basename6 = path.basename(filePath);
|
|
29969
|
+
if (!allowedBasenames.has(basename6)) {
|
|
29846
29970
|
continue;
|
|
29847
29971
|
}
|
|
29848
29972
|
}
|
|
@@ -31601,6 +31725,11 @@ isolation scope: Only create or modify files in the test/ directory. Tests must
|
|
|
31601
31725
|
isolation scope: Create test files in test/. MAY read src/ files and MAY import from src/ to ensure correct types/interfaces. May create minimal stubs in src/ if needed to make imports work, but do NOT implement real logic.${footer}`;
|
|
31602
31726
|
}
|
|
31603
31727
|
if (role === "implementer") {
|
|
31728
|
+
if (mode === "lite") {
|
|
31729
|
+
return `${header}
|
|
31730
|
+
|
|
31731
|
+
isolation scope: Implement source code in src/ to make tests pass. You MAY add tests for acceptance criteria that have no coverage yet; do NOT weaken, delete, or skip existing tests. Run tests frequently to track progress.${footer}`;
|
|
31732
|
+
}
|
|
31604
31733
|
return `${header}
|
|
31605
31734
|
|
|
31606
31735
|
isolation scope: Implement source code in src/ to make tests pass. Do not modify test files. Run tests frequently to track progress.${footer}`;
|
|
@@ -32167,12 +32296,36 @@ Include the story ID when known \u2014 \`feat(<story-id>): <description>\`.
|
|
|
32167
32296
|
When the story is ambiguous, pick an interpretation, proceed, and document the choice in the commit body under \`Assumptions:\`. Do not invent requirements; do not silently choose when the story is genuinely under-specified \u2014 note it.`;
|
|
32168
32297
|
}
|
|
32169
32298
|
|
|
32299
|
+
// src/prompts/sections/test-quality.ts
|
|
32300
|
+
function buildTestQualitySection(role, variant, storyId) {
|
|
32301
|
+
const authors = AUTHORING_ROLES.has(role) || role === "implementer" && variant === "lite";
|
|
32302
|
+
if (!authors)
|
|
32303
|
+
return "";
|
|
32304
|
+
const storyIdLine = storyId ? `
|
|
32305
|
+
- Test names must use THIS story's ID (${storyId}) \u2014 never a sibling story's ID copied from a nearby test.` : "";
|
|
32306
|
+
return `# Review-Proof Tests
|
|
32307
|
+
|
|
32308
|
+
An adversarial reviewer will audit your tests after implementation and BLOCK the story on any test-gap finding. Each block costs a full rectification round. Write tests that survive that audit the first time:
|
|
32309
|
+
|
|
32310
|
+
- Every acceptance criterion needs a test that INVOKES the code at runtime and asserts its observable behavior (return value, thrown error, rendered/mounted output, emitted event, persisted state).
|
|
32311
|
+
- NEVER write source-inspection tests \u2014 asserting that a file contains a pattern, string, or symbol proves nothing about behavior and WILL be blocked as test-gap.
|
|
32312
|
+
- No placeholder or tautological tests: \`expect(true).toBe(true)\`, asserting on literals, empty bodies, \`.skip\`/\`.todo\` on an AC-covering test \u2014 all blocked as test-gap.
|
|
32313
|
+
- Every new exported symbol an AC depends on must be exercised by at least one test.
|
|
32314
|
+
- Cover the boundary and error paths the reviewer probes: empty/null/zero/negative inputs, and failure modes (errors must surface, not be swallowed).
|
|
32315
|
+
- For UI/page-level ACs, mount the page/component and simulate the interaction \u2014 a test that only exercises the underlying unit reads as no coverage for the AC's wiring.${storyIdLine}`;
|
|
32316
|
+
}
|
|
32317
|
+
var AUTHORING_ROLES;
|
|
32318
|
+
var init_test_quality = __esm(() => {
|
|
32319
|
+
AUTHORING_ROLES = new Set(["test-writer", "single-session", "tdd-simple", "batch"]);
|
|
32320
|
+
});
|
|
32321
|
+
|
|
32170
32322
|
// src/prompts/sections/index.ts
|
|
32171
32323
|
var init_sections2 = __esm(() => {
|
|
32172
32324
|
init_hermetic();
|
|
32173
32325
|
init_role_task();
|
|
32174
32326
|
init_story();
|
|
32175
32327
|
init_acceptance();
|
|
32328
|
+
init_test_quality();
|
|
32176
32329
|
});
|
|
32177
32330
|
|
|
32178
32331
|
// src/prompts/loader.ts
|
|
@@ -32315,7 +32468,7 @@ class TddPromptBuilder {
|
|
|
32315
32468
|
if (this.role === "verifier" && this.story_) {
|
|
32316
32469
|
acc.add(this.s("verdict", buildVerdictSection(this.story_)));
|
|
32317
32470
|
}
|
|
32318
|
-
const isolation = this.options.isolation;
|
|
32471
|
+
const isolation = this.role === "implementer" && this.options.variant === "lite" ? "lite" : this.options.isolation;
|
|
32319
32472
|
acc.add(this.s("isolation", buildIsolationSection(this.role, isolation, this.testCommand_)));
|
|
32320
32473
|
const tddLang = buildTddLanguageSection(this.loaderConfig_?.project?.language);
|
|
32321
32474
|
if (tddLang)
|
|
@@ -32331,6 +32484,9 @@ class TddPromptBuilder {
|
|
|
32331
32484
|
const guardrails = buildBehavioralGuardrailsSection(this.role, guardrailLevel, guardrailVariant, guardrailIsolation);
|
|
32332
32485
|
if (guardrails)
|
|
32333
32486
|
acc.add(this.s("guardrails", guardrails));
|
|
32487
|
+
const testQuality = buildTestQualitySection(this.role, this.options.variant, this.story_?.id);
|
|
32488
|
+
if (testQuality)
|
|
32489
|
+
acc.add(this.s("test-quality", testQuality));
|
|
32334
32490
|
if (this.role !== "verifier") {
|
|
32335
32491
|
const selfVerify = buildSelfVerificationSection(this.role, this.selfVerification_);
|
|
32336
32492
|
if (selfVerify)
|
|
@@ -32782,6 +32938,32 @@ var init_debate_builder = __esm(() => {
|
|
|
32782
32938
|
RE_REVIEW_JSON_DIRECTIVE = `Respond with JSON: { passed: boolean; findings: Array<${FINDING_SCHEMA}>; findingReasoning: { [ruleId: string]: string }; deltaSummary: string }`;
|
|
32783
32939
|
});
|
|
32784
32940
|
|
|
32941
|
+
// src/review/semantic-categories.ts
|
|
32942
|
+
function isSemanticCategory(value) {
|
|
32943
|
+
return SEMANTIC_CATEGORY_SET.has(value);
|
|
32944
|
+
}
|
|
32945
|
+
function normalizeSemanticCategory(raw) {
|
|
32946
|
+
if (typeof raw !== "string")
|
|
32947
|
+
return "";
|
|
32948
|
+
const normalized = raw.trim().toLowerCase();
|
|
32949
|
+
if (normalized === "")
|
|
32950
|
+
return "";
|
|
32951
|
+
return isSemanticCategory(normalized) ? normalized : "other";
|
|
32952
|
+
}
|
|
32953
|
+
var SEMANTIC_CATEGORIES, SEMANTIC_CATEGORY_SET, SEMANTIC_CATEGORY_ENUM_LINE;
|
|
32954
|
+
var init_semantic_categories = __esm(() => {
|
|
32955
|
+
SEMANTIC_CATEGORIES = [
|
|
32956
|
+
"unimplemented",
|
|
32957
|
+
"partial",
|
|
32958
|
+
"contradiction",
|
|
32959
|
+
"dead-path",
|
|
32960
|
+
"unwired",
|
|
32961
|
+
"other"
|
|
32962
|
+
];
|
|
32963
|
+
SEMANTIC_CATEGORY_SET = new Set(SEMANTIC_CATEGORIES);
|
|
32964
|
+
SEMANTIC_CATEGORY_ENUM_LINE = SEMANTIC_CATEGORIES.map((c) => `"${c}"`).join(" | ");
|
|
32965
|
+
});
|
|
32966
|
+
|
|
32785
32967
|
// src/prompts/builders/prior-iterations-builder.ts
|
|
32786
32968
|
function buildPriorIterationsBlock(iterations) {
|
|
32787
32969
|
if (iterations.length === 0)
|
|
@@ -32840,9 +33022,13 @@ function renderVerdictTemplate(iterations) {
|
|
|
32840
33022
|
When outcome is "unchanged", the prior hypothesis is FALSIFIED \u2014 the change did not affect what was tested. Choose a different category before producing a new verdict. Do NOT repeat fixes listed above.` : "";
|
|
32841
33023
|
return [
|
|
32842
33024
|
`**Required:** before adding any new finding, classify each of the ${total} prior finding(s) above as one of:`,
|
|
32843
|
-
"- `addressed` \u2014 the current diff resolves it (
|
|
32844
|
-
"- `still-blocking` \u2014 the implementer did not fix it; re-flag it with the IDENTICAL `file`, `line`, `category`, and substantively the same `message` wording",
|
|
32845
|
-
|
|
33025
|
+
"- `addressed` \u2014 the current diff resolves it; record it in `acks` (not `findings`), citing the diff line that fixes it in `note`",
|
|
33026
|
+
"- `still-blocking` \u2014 the implementer did not fix it; re-flag it in `findings` with the IDENTICAL `file`, `line`, `category`, and substantively the same `message` wording",
|
|
33027
|
+
"- `never-an-issue` \u2014 your prior judgment was wrong; record it in `acks` (not `findings`) and explain why in `note`",
|
|
33028
|
+
"",
|
|
33029
|
+
"Do NOT emit an acknowledgement as a finding. A resolved or withdrawn prior finding is not a defect \u2014",
|
|
33030
|
+
"reporting it as one inflates the finding count and buries the real defects. Only `still-blocking` belongs in `findings`.",
|
|
33031
|
+
"",
|
|
32846
33032
|
`Then surface any genuinely new findings.${unchangedNote}`
|
|
32847
33033
|
].join(`
|
|
32848
33034
|
`);
|
|
@@ -32906,13 +33092,36 @@ Flag issues only when you have confirmed:
|
|
|
32906
33092
|
3. New code has dead paths that will never execute (stubs, noops, unreachable branches)
|
|
32907
33093
|
4. New code is not wired into callers/exports (verified by grepping for usage)
|
|
32908
33094
|
|
|
32909
|
-
Do NOT flag: style issues, naming conventions, import ordering, file length, or anything lint handles
|
|
33095
|
+
Do NOT flag: style issues, naming conventions, import ordering, file length, or anything lint handles.
|
|
33096
|
+
|
|
33097
|
+
**Finding categories \u2014 every finding MUST carry exactly one \`category\`:**
|
|
33098
|
+
- \`unimplemented\` \u2014 an AC has no implementation at all.
|
|
33099
|
+
- \`partial\` \u2014 an AC is implemented for some inputs or paths, but not everything it specifies.
|
|
33100
|
+
- \`contradiction\` \u2014 the implementation does the opposite of what the AC specifies, or something the AC forbids.
|
|
33101
|
+
- \`dead-path\` \u2014 a stub, noop, or unreachable branch that will never execute.
|
|
33102
|
+
- \`unwired\` \u2014 new code exists but is not reachable: not exported, not called, not registered.
|
|
33103
|
+
- \`other\` \u2014 genuinely AC-related, but none of the above.
|
|
33104
|
+
|
|
33105
|
+
Pick the most specific axis that fits. Do not invent categories outside this list \u2014 an unrecognised value is recorded as \`other\` and loses its signal.`, SEMANTIC_OUTPUT_SCHEMA, ReviewPromptBuilder;
|
|
33106
|
+
var init_review_builder = __esm(() => {
|
|
33107
|
+
init_semantic_categories();
|
|
33108
|
+
init_sections2();
|
|
33109
|
+
SEMANTIC_ROLE = "You are a semantic code reviewer with access to the repository files. " + "Your job is to walk each acceptance criterion (AC) and judge whether the production code fulfills it \u2014 fully, partially, or not at all. " + "Test coverage gaps and convention/lint issues are out of scope \u2014 adversarial review and lint/typecheck handle those.";
|
|
33110
|
+
SEMANTIC_OUTPUT_SCHEMA = `Respond with JSON only \u2014 no explanation text before or after:
|
|
32910
33111
|
{
|
|
32911
33112
|
"passed": boolean,
|
|
32912
33113
|
"inspectedFiles": ["relative/path/you/actually/opened.ts"],
|
|
33114
|
+
"acks": [
|
|
33115
|
+
{
|
|
33116
|
+
"priorFinding": "<short identifier \u2014 file:line or a few words of its message>",
|
|
33117
|
+
"status": "addressed" | "never-an-issue",
|
|
33118
|
+
"note": "<why: the diff line that fixes it, or why the prior judgment was wrong>"
|
|
33119
|
+
}
|
|
33120
|
+
],
|
|
32913
33121
|
"findings": [
|
|
32914
33122
|
{
|
|
32915
33123
|
"severity": "error" | "warning" | "info" | "unverifiable",
|
|
33124
|
+
"category": ${SEMANTIC_CATEGORY_ENUM_LINE},
|
|
32916
33125
|
"file": "path/to/file",
|
|
32917
33126
|
"line": 42,
|
|
32918
33127
|
"issue": "description of the issue",
|
|
@@ -32930,14 +33139,13 @@ Do NOT flag: style issues, naming conventions, import ordering, file length, or
|
|
|
32930
33139
|
}
|
|
32931
33140
|
|
|
32932
33141
|
Notes:
|
|
33142
|
+
- \`acks\` records prior findings you are NOT re-flagging \u2014 resolved by the diff, or withdrawn. Omit it (or use \`[]\`) when there are no prior findings. An acknowledgement must never appear in \`findings\`.
|
|
33143
|
+
- \`category\` is required on every finding \u2014 one of the axes listed above.
|
|
32933
33144
|
- \`acIndex\` is required when severity is "error" (1-based, into the Acceptance Criteria list above).
|
|
32934
33145
|
- \`acQuote\` is optional advisory metadata for human auditors \u2014 not validated.
|
|
32935
33146
|
- Omit both for "warning", "info", "unverifiable".
|
|
32936
33147
|
- \`inspectedFiles\` must list the relative paths you actually opened while reviewing. A \`passed:true\` verdict with an empty or absent \`inspectedFiles\` is invalid \u2014 walk each AC against the real files before passing.
|
|
32937
|
-
If all ACs are correctly implemented after inspecting the code, respond with { "passed": true, "inspectedFiles": ["..."], "findings": [] }
|
|
32938
|
-
var init_review_builder = __esm(() => {
|
|
32939
|
-
init_sections2();
|
|
32940
|
-
SEMANTIC_ROLE = "You are a semantic code reviewer with access to the repository files. " + "Your job is to walk each acceptance criterion (AC) and judge whether the production code fulfills it \u2014 fully, partially, or not at all. " + "Test coverage gaps and convention/lint issues are out of scope \u2014 adversarial review and lint/typecheck handle those.";
|
|
33148
|
+
If all ACs are correctly implemented after inspecting the code, respond with { "passed": true, "inspectedFiles": ["..."], "findings": [] }.`;
|
|
32941
33149
|
ReviewPromptBuilder = class ReviewPromptBuilder {
|
|
32942
33150
|
buildSemanticReviewPrompt(story, semanticConfig, options) {
|
|
32943
33151
|
const acList = story.acceptanceCriteria.map((ac, i) => `${i + 1}. ${ac}`).join(`
|
|
@@ -32987,7 +33195,7 @@ Respond with a condensed summary:
|
|
|
32987
33195
|
- ${advisoryClause}
|
|
32988
33196
|
- Keep \`verifiedBy\` for every finding. If \`verifiedBy.observed\` is long, abbreviate it to one line \u2014 never drop the field.
|
|
32989
33197
|
Output ONLY a complete, valid JSON object. It must start with { and end with }.
|
|
32990
|
-
Schema: {"passed": boolean, "findings": [{"severity": string, "category":
|
|
33198
|
+
Schema: {"passed": boolean, "findings": [{"severity": string, "category": ${SEMANTIC_CATEGORY_ENUM_LINE}, "file": string, "line": number, "issue": string, "suggestion": string, "verifiedBy": {"command": string, "file": string, "line": number, "observed": string}}]}`;
|
|
32991
33199
|
}
|
|
32992
33200
|
static demandInspection() {
|
|
32993
33201
|
return `Your previous review returned \`passed:true\` with no findings and an empty (or absent) \`inspectedFiles\` list. That means you did not open any of the changed files \u2014 a verdict reached without reading the code is not valid.
|
|
@@ -33042,6 +33250,7 @@ ${drops.map((d, i) => `${i + 1}. [${d.finding.severity}] ${d.finding.issue}`).jo
|
|
|
33042
33250
|
Please re-review the code and re-issue any valid findings. For each finding you re-issue:
|
|
33043
33251
|
- You MUST include a valid \`acIndex\` (1-based index into the AC list below)
|
|
33044
33252
|
- You MUST include a \`verifiedBy\` field with verified evidence
|
|
33253
|
+
- You MUST include a \`category\`: ${SEMANTIC_CATEGORY_ENUM_LINE}
|
|
33045
33254
|
|
|
33046
33255
|
## Acceptance Criteria
|
|
33047
33256
|
${acList}
|
|
@@ -33176,6 +33385,7 @@ What new exported units lack corresponding test files?
|
|
|
33176
33385
|
- Bodies that always pass: \`expect(true).toBe(true)\`, \`expect(x).toBe(x)\`, \`expect(1).toBe(1)\`, an empty test body, or \`assert(true)\`.
|
|
33177
33386
|
- Tests skipped/disabled (\`it.skip\`, \`test.todo\`, \`xit\`, commented-out assertions) that an AC depends on.
|
|
33178
33387
|
- Assertions that never exercise the implementation (e.g. asserting on a literal, not on a value the production code produced).
|
|
33388
|
+
- Source-inspection tests: reading a source file and asserting it contains a pattern, string, or symbol instead of invoking the code and asserting its runtime behavior.
|
|
33179
33389
|
|
|
33180
33390
|
For each such finding: set \`acIndex\` to the AC the fake test purports to cover, \`acQuote\` to a verbatim substring of that AC, and \`verifiedBy.observed\` to the placeholder line itself (e.g. \`expect(true).toBe(true)\`). Do **not** downgrade these to \`warning\` \u2014 a green suite built on placeholder assertions is a failing implementation with hidden evidence.
|
|
33181
33391
|
|
|
@@ -33199,6 +33409,13 @@ Respond with ONLY a JSON object \u2014 no preamble, no explanation outside the J
|
|
|
33199
33409
|
{
|
|
33200
33410
|
"passed": true | false,
|
|
33201
33411
|
"inspectedFiles": ["relative/path/you/actually/opened.ts"],
|
|
33412
|
+
"acks": [
|
|
33413
|
+
{
|
|
33414
|
+
"priorFinding": "<short identifier of the prior finding \u2014 its file:line or a few words of its message>",
|
|
33415
|
+
"status": "addressed" | "never-an-issue",
|
|
33416
|
+
"note": "<why: the diff line that fixes it, or why the prior judgment was wrong>"
|
|
33417
|
+
}
|
|
33418
|
+
],
|
|
33202
33419
|
"findings": [
|
|
33203
33420
|
{
|
|
33204
33421
|
"severity": "error" | "warning" | "info" | "unverifiable",
|
|
@@ -33225,6 +33442,8 @@ Respond with ONLY a JSON object \u2014 no preamble, no explanation outside the J
|
|
|
33225
33442
|
|
|
33226
33443
|
**No rubber-stamping:** \`inspectedFiles\` must list the relative paths you actually opened with your tools while reviewing. A \`passed:true\` verdict with an empty or absent \`inspectedFiles\` is invalid \u2014 it means you never looked at the code. Fetch the diff and open the changed files before forming any verdict.
|
|
33227
33444
|
|
|
33445
|
+
\`acks\` records prior findings you are NOT re-flagging \u2014 resolved by the diff, or withdrawn. Omit it (or use \`[]\`) when there are no prior findings. An acknowledgement must never appear in \`findings\`.
|
|
33446
|
+
|
|
33228
33447
|
Severity guide:
|
|
33229
33448
|
- \`"error"\`: confident this will cause real failure or regression
|
|
33230
33449
|
- \`"warning"\`: fragile or incomplete but may ship without immediate breakage
|
|
@@ -33769,8 +33988,8 @@ function stripMarkdownInline(s) {
|
|
|
33769
33988
|
function extractLocusKeywords(finding) {
|
|
33770
33989
|
const keywords = [];
|
|
33771
33990
|
if (finding.file) {
|
|
33772
|
-
const
|
|
33773
|
-
const stem =
|
|
33991
|
+
const basename6 = finding.file.split("/").pop() ?? "";
|
|
33992
|
+
const stem = basename6.replace(/\.[^.]+$/, "");
|
|
33774
33993
|
for (const part of stem.split(/[-_]/)) {
|
|
33775
33994
|
if (part.length >= 3)
|
|
33776
33995
|
keywords.push(part.toLowerCase());
|
|
@@ -35919,6 +36138,35 @@ var init_acceptance_fix = __esm(() => {
|
|
|
35919
36138
|
};
|
|
35920
36139
|
});
|
|
35921
36140
|
|
|
36141
|
+
// src/review/acks.ts
|
|
36142
|
+
function extractAcks(raw) {
|
|
36143
|
+
if (!Array.isArray(raw))
|
|
36144
|
+
return [];
|
|
36145
|
+
const acks = [];
|
|
36146
|
+
for (const entry of raw) {
|
|
36147
|
+
if (acks.length >= MAX_ACKS)
|
|
36148
|
+
break;
|
|
36149
|
+
if (typeof entry === "string") {
|
|
36150
|
+
if (entry !== "")
|
|
36151
|
+
acks.push({ priorFinding: entry, status: "unknown" });
|
|
36152
|
+
continue;
|
|
36153
|
+
}
|
|
36154
|
+
if (typeof entry !== "object" || entry === null)
|
|
36155
|
+
continue;
|
|
36156
|
+
const e = entry;
|
|
36157
|
+
const known = e.status === "addressed" || e.status === "never-an-issue";
|
|
36158
|
+
const note = typeof e.note === "string" ? e.note.slice(0, MAX_NOTE_CHARS) : "";
|
|
36159
|
+
acks.push({
|
|
36160
|
+
priorFinding: typeof e.priorFinding === "string" ? e.priorFinding : "",
|
|
36161
|
+
status: known ? e.status : "unknown",
|
|
36162
|
+
...note !== "" && { note },
|
|
36163
|
+
...!known && typeof e.status === "string" && { rawStatus: e.status }
|
|
36164
|
+
});
|
|
36165
|
+
}
|
|
36166
|
+
return acks;
|
|
36167
|
+
}
|
|
36168
|
+
var MAX_ACKS = 50, MAX_NOTE_CHARS = 500;
|
|
36169
|
+
|
|
35922
36170
|
// src/review/ac-structural-counterfactual.ts
|
|
35923
36171
|
function analyzeStructuralCounterfactual(finding, acceptanceCriteria, diffFiles) {
|
|
35924
36172
|
const acIndexInRange = typeof finding.acIndex === "number" && finding.acIndex >= 1 && finding.acIndex <= acceptanceCriteria.length;
|
|
@@ -35956,7 +36204,12 @@ function validateAdversarialShape(parsed) {
|
|
|
35956
36204
|
return null;
|
|
35957
36205
|
if (!Array.isArray(obj.findings))
|
|
35958
36206
|
return null;
|
|
35959
|
-
|
|
36207
|
+
const acks = extractAcks(obj.acks);
|
|
36208
|
+
return {
|
|
36209
|
+
passed: obj.passed,
|
|
36210
|
+
findings: obj.findings,
|
|
36211
|
+
...acks.length > 0 && { acks }
|
|
36212
|
+
};
|
|
35960
36213
|
}
|
|
35961
36214
|
function formatFindings(findings) {
|
|
35962
36215
|
return findings.map((f) => `[${f.severity}][${f.category}] ${f.file}:${f.line} \u2014 ${f.issue}
|
|
@@ -36011,7 +36264,21 @@ function validateLLMShape(parsed) {
|
|
|
36011
36264
|
return null;
|
|
36012
36265
|
if (!Array.isArray(obj.findings))
|
|
36013
36266
|
return null;
|
|
36014
|
-
|
|
36267
|
+
const acks = extractAcks(obj.acks);
|
|
36268
|
+
return {
|
|
36269
|
+
passed: obj.passed,
|
|
36270
|
+
findings: obj.findings.filter(isFindingShaped).map(withNormalizedCategory),
|
|
36271
|
+
...acks.length > 0 && { acks }
|
|
36272
|
+
};
|
|
36273
|
+
}
|
|
36274
|
+
function isFindingShaped(f) {
|
|
36275
|
+
return typeof f === "object" && f !== null && !Array.isArray(f);
|
|
36276
|
+
}
|
|
36277
|
+
function withNormalizedCategory(f) {
|
|
36278
|
+
const category = normalizeSemanticCategory(f.category);
|
|
36279
|
+
if (category === "")
|
|
36280
|
+
return f;
|
|
36281
|
+
return { ...f, category };
|
|
36015
36282
|
}
|
|
36016
36283
|
function parseLLMResponse(raw) {
|
|
36017
36284
|
try {
|
|
@@ -36067,7 +36334,7 @@ function llmFindingToFinding(f, opts = {}) {
|
|
|
36067
36334
|
return {
|
|
36068
36335
|
source: "semantic-review",
|
|
36069
36336
|
severity: normalizeSeverity2(f.severity),
|
|
36070
|
-
category:
|
|
36337
|
+
category: normalizeSemanticCategory(f.category),
|
|
36071
36338
|
file: f.file,
|
|
36072
36339
|
line: f.line,
|
|
36073
36340
|
message: f.issue,
|
|
@@ -36082,6 +36349,7 @@ function toReviewFindings(findings, opts = {}) {
|
|
|
36082
36349
|
var UNVERIFIED_FINDING_PATTERNS;
|
|
36083
36350
|
var init_semantic_helpers = __esm(() => {
|
|
36084
36351
|
init_category_fix_target();
|
|
36352
|
+
init_semantic_categories();
|
|
36085
36353
|
init_severity();
|
|
36086
36354
|
UNVERIFIED_FINDING_PATTERNS = [
|
|
36087
36355
|
"cannot verify",
|
|
@@ -36448,6 +36716,7 @@ async function performSemanticReground(turn, firstParsed, drops, ctx) {
|
|
|
36448
36716
|
output: JSON.stringify({
|
|
36449
36717
|
passed: false,
|
|
36450
36718
|
findings: secondParsed.findings,
|
|
36719
|
+
...secondParsed.acks && { acks: secondParsed.acks },
|
|
36451
36720
|
_repromptInfo: { dropCount, outcome: "recovered-blocking", costUsd }
|
|
36452
36721
|
}),
|
|
36453
36722
|
estimatedCostUsd: costUsd
|
|
@@ -36460,6 +36729,7 @@ async function performSemanticReground(turn, firstParsed, drops, ctx) {
|
|
|
36460
36729
|
output: JSON.stringify({
|
|
36461
36730
|
passed: true,
|
|
36462
36731
|
findings: [...firstAdvisory, ...secondAdvisory],
|
|
36732
|
+
...secondParsed.acks && { acks: secondParsed.acks },
|
|
36463
36733
|
_repromptInfo: { dropCount, outcome: "recovered-advisory-only", costUsd }
|
|
36464
36734
|
}),
|
|
36465
36735
|
estimatedCostUsd: costUsd
|
|
@@ -36558,7 +36828,7 @@ var FAIL_OPEN, SEMANTIC_REQUOTE_RECOVERED_EVENT = "review.semantic.finding.requo
|
|
|
36558
36828
|
const passed = !requoted.findings.some((finding) => isBlockingSeverity(finding.severity, ctx.input.blockingThreshold ?? "error"));
|
|
36559
36829
|
return {
|
|
36560
36830
|
...turn,
|
|
36561
|
-
output: JSON.stringify({ passed, findings: requoted.findings }),
|
|
36831
|
+
output: JSON.stringify({ passed, findings: requoted.findings, ...parsed.acks && { acks: parsed.acks } }),
|
|
36562
36832
|
estimatedCostUsd: (turn.estimatedCostUsd ?? 0) + requoted.extraCostUsd
|
|
36563
36833
|
};
|
|
36564
36834
|
}
|
|
@@ -36635,7 +36905,8 @@ var init_semantic_review = __esm(() => {
|
|
|
36635
36905
|
findings: parsed.findings,
|
|
36636
36906
|
normalizedFindings: [],
|
|
36637
36907
|
acDropped: [],
|
|
36638
|
-
repromptEvent
|
|
36908
|
+
repromptEvent,
|
|
36909
|
+
...parsed.acks && { acks: parsed.acks }
|
|
36639
36910
|
};
|
|
36640
36911
|
}
|
|
36641
36912
|
const unparsedPreview = previewOutput(output, UNPARSED_PREVIEW_BYTES);
|
|
@@ -36686,7 +36957,7 @@ var init_semantic_review = __esm(() => {
|
|
|
36686
36957
|
};
|
|
36687
36958
|
});
|
|
36688
36959
|
|
|
36689
|
-
// src/operations/adversarial-
|
|
36960
|
+
// src/operations/adversarial-reprompt-marker.ts
|
|
36690
36961
|
function withRepromptMarker2(output, info) {
|
|
36691
36962
|
const parsed = tryParseLLMJson(output);
|
|
36692
36963
|
if (!parsed || typeof parsed !== "object")
|
|
@@ -36709,6 +36980,9 @@ function extractRepromptInfo2(raw) {
|
|
|
36709
36980
|
outcome: i.outcome
|
|
36710
36981
|
};
|
|
36711
36982
|
}
|
|
36983
|
+
var init_adversarial_reprompt_marker = () => {};
|
|
36984
|
+
|
|
36985
|
+
// src/operations/adversarial-review.ts
|
|
36712
36986
|
async function requoteBlockingAdversarialFindings(findings, ctx) {
|
|
36713
36987
|
const threshold = ctx.input.blockingThreshold ?? "error";
|
|
36714
36988
|
const maxRequotes = ctx.input.adversarialConfig.substantiation?.maxRequotes ?? DEFAULT_MAX_REQUOTES2;
|
|
@@ -36823,6 +37097,7 @@ async function performAdversarialReground(turn, firstParsed, drops, ctx) {
|
|
|
36823
37097
|
output: JSON.stringify({
|
|
36824
37098
|
passed: false,
|
|
36825
37099
|
findings: secondParsed.findings,
|
|
37100
|
+
...secondParsed.acks && { acks: secondParsed.acks },
|
|
36826
37101
|
_repromptInfo: { dropCount, outcome: "recovered-blocking", costUsd }
|
|
36827
37102
|
}),
|
|
36828
37103
|
estimatedCostUsd: costUsd
|
|
@@ -36835,6 +37110,7 @@ async function performAdversarialReground(turn, firstParsed, drops, ctx) {
|
|
|
36835
37110
|
output: JSON.stringify({
|
|
36836
37111
|
passed: true,
|
|
36837
37112
|
findings: [...firstAdvisory, ...secondAdvisory],
|
|
37113
|
+
...secondParsed.acks && { acks: secondParsed.acks },
|
|
36838
37114
|
_repromptInfo: { dropCount, outcome: "recovered-advisory-only", costUsd }
|
|
36839
37115
|
}),
|
|
36840
37116
|
estimatedCostUsd: costUsd
|
|
@@ -36884,6 +37160,7 @@ var init_adversarial_review = __esm(() => {
|
|
|
36884
37160
|
init_recurrence_demotion();
|
|
36885
37161
|
init_requote_response();
|
|
36886
37162
|
init__review_fallback();
|
|
37163
|
+
init_adversarial_reprompt_marker();
|
|
36887
37164
|
FAIL_OPEN2 = {
|
|
36888
37165
|
passed: true,
|
|
36889
37166
|
findings: [],
|
|
@@ -36928,7 +37205,7 @@ var init_adversarial_review = __esm(() => {
|
|
|
36928
37205
|
const passed = !requoted.findings.some((finding) => isBlockingSeverity(finding.severity, ctx.input.blockingThreshold ?? "error"));
|
|
36929
37206
|
return {
|
|
36930
37207
|
...turn,
|
|
36931
|
-
output: JSON.stringify({ passed, findings: requoted.findings }),
|
|
37208
|
+
output: JSON.stringify({ passed, findings: requoted.findings, ...parsed.acks && { acks: parsed.acks } }),
|
|
36932
37209
|
estimatedCostUsd: (turn.estimatedCostUsd ?? 0) + requoted.extraCostUsd
|
|
36933
37210
|
};
|
|
36934
37211
|
}
|
|
@@ -36963,7 +37240,8 @@ var init_adversarial_review = __esm(() => {
|
|
|
36963
37240
|
findings: parsed.findings,
|
|
36964
37241
|
normalizedFindings: [],
|
|
36965
37242
|
acDropped: [],
|
|
36966
|
-
repromptEvent
|
|
37243
|
+
repromptEvent,
|
|
37244
|
+
...parsed.acks && { acks: parsed.acks }
|
|
36967
37245
|
};
|
|
36968
37246
|
}
|
|
36969
37247
|
if (/"passed"\s*:\s*false/.test(output) && !/"findings"\s*:\s*\[\s*\{/.test(output)) {
|
|
@@ -42299,12 +42577,50 @@ function recordAdversarialAudit(opts) {
|
|
|
42299
42577
|
blockingThreshold: opts.blockingThreshold,
|
|
42300
42578
|
result: opts.result,
|
|
42301
42579
|
advisoryFindings: opts.advisoryFindings,
|
|
42580
|
+
acks: opts.acks,
|
|
42302
42581
|
diffAvailable: opts.diffAvailable,
|
|
42303
42582
|
adversarialDropAnalysis: opts.adversarialDropAnalysis,
|
|
42304
42583
|
adversarialAcceptAnalysis: opts.adversarialAcceptAnalysis
|
|
42305
42584
|
});
|
|
42306
42585
|
}
|
|
42307
42586
|
|
|
42587
|
+
// src/review/adversarial-counterfactual-telemetry.ts
|
|
42588
|
+
function buildCounterfactualTelemetry({
|
|
42589
|
+
acDropped,
|
|
42590
|
+
blockingFindings,
|
|
42591
|
+
acceptanceCriteria,
|
|
42592
|
+
diffFiles
|
|
42593
|
+
}) {
|
|
42594
|
+
const adversarialDropAnalysis = acDropped.map((d) => ({
|
|
42595
|
+
finding: {
|
|
42596
|
+
file: d.finding.file ?? "<unknown>",
|
|
42597
|
+
line: d.finding.line ?? 0,
|
|
42598
|
+
severity: d.finding.severity,
|
|
42599
|
+
category: d.finding.category ?? "<unknown>",
|
|
42600
|
+
issue: d.finding.issue
|
|
42601
|
+
},
|
|
42602
|
+
dropCode: d.code,
|
|
42603
|
+
acIndex: d.finding.acIndex,
|
|
42604
|
+
rawCategory: d.finding.category ?? "",
|
|
42605
|
+
counterfactual: analyzeStructuralCounterfactual({ acIndex: d.finding.acIndex, category: d.finding.category, file: d.finding.file }, acceptanceCriteria, diffFiles)
|
|
42606
|
+
}));
|
|
42607
|
+
const adversarialAcceptAnalysis = blockingFindings.map((f) => ({
|
|
42608
|
+
finding: {
|
|
42609
|
+
file: f.file,
|
|
42610
|
+
line: f.line,
|
|
42611
|
+
severity: f.severity,
|
|
42612
|
+
category: f.category
|
|
42613
|
+
},
|
|
42614
|
+
acIndex: f.acIndex,
|
|
42615
|
+
rawCategory: f.category,
|
|
42616
|
+
counterfactual: analyzeStructuralCounterfactual({ acIndex: f.acIndex, category: f.category, file: f.file }, acceptanceCriteria, diffFiles)
|
|
42617
|
+
}));
|
|
42618
|
+
return { adversarialDropAnalysis, adversarialAcceptAnalysis };
|
|
42619
|
+
}
|
|
42620
|
+
var init_adversarial_counterfactual_telemetry = __esm(() => {
|
|
42621
|
+
init_ac_structural_counterfactual();
|
|
42622
|
+
});
|
|
42623
|
+
|
|
42308
42624
|
// src/review/diff-utils.ts
|
|
42309
42625
|
var {spawn: spawn3 } = globalThis.Bun;
|
|
42310
42626
|
async function resolveNaxIgnorePathspecExcludes(workdir, options) {
|
|
@@ -42703,7 +43019,7 @@ var package_default;
|
|
|
42703
43019
|
var init_package = __esm(() => {
|
|
42704
43020
|
package_default = {
|
|
42705
43021
|
name: "@nathapp/nax",
|
|
42706
|
-
version: "0.
|
|
43022
|
+
version: "0.76.0",
|
|
42707
43023
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
42708
43024
|
type: "module",
|
|
42709
43025
|
bin: {
|
|
@@ -42807,8 +43123,8 @@ var init_version = __esm(() => {
|
|
|
42807
43123
|
NAX_VERSION = package_default.version;
|
|
42808
43124
|
NAX_COMMIT = (() => {
|
|
42809
43125
|
try {
|
|
42810
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
42811
|
-
return "
|
|
43126
|
+
if (/^[0-9a-f]{6,10}$/.test("e7721ea5"))
|
|
43127
|
+
return "e7721ea5";
|
|
42812
43128
|
} catch {}
|
|
42813
43129
|
try {
|
|
42814
43130
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -42855,6 +43171,7 @@ function toPersistedEntry(entry, epochMs) {
|
|
|
42855
43171
|
blockingThreshold: entry.blockingThreshold ?? "error",
|
|
42856
43172
|
result: entry.result,
|
|
42857
43173
|
advisoryFindings: entry.advisoryFindings ?? null,
|
|
43174
|
+
acks: entry.acks ?? null,
|
|
42858
43175
|
acDropped: entry.acDropped ?? null,
|
|
42859
43176
|
...entry.parsed ? {} : { unparsedPreview: entry.unparsedPreview ?? null },
|
|
42860
43177
|
diffAvailable: entry.diffAvailable ?? null,
|
|
@@ -43225,6 +43542,7 @@ async function runAdversarialReview(opts) {
|
|
|
43225
43542
|
...tagCoverageGap(toAdversarialReviewFindings(demoted, { isTestFile: testFileMatch }))
|
|
43226
43543
|
];
|
|
43227
43544
|
const acDropped = opResult.acDropped ?? [];
|
|
43545
|
+
const acks = opResult.acks;
|
|
43228
43546
|
let diffFiles;
|
|
43229
43547
|
let diffAvailable;
|
|
43230
43548
|
if (diff && diff.length > 0) {
|
|
@@ -43242,30 +43560,12 @@ async function runAdversarialReview(opts) {
|
|
|
43242
43560
|
diffAvailable = true;
|
|
43243
43561
|
}
|
|
43244
43562
|
}
|
|
43245
|
-
const adversarialDropAnalysis
|
|
43246
|
-
|
|
43247
|
-
|
|
43248
|
-
|
|
43249
|
-
|
|
43250
|
-
|
|
43251
|
-
issue: d.finding.issue
|
|
43252
|
-
},
|
|
43253
|
-
dropCode: d.code,
|
|
43254
|
-
acIndex: d.finding.acIndex,
|
|
43255
|
-
rawCategory: d.finding.category ?? "",
|
|
43256
|
-
counterfactual: analyzeStructuralCounterfactual({ acIndex: d.finding.acIndex, category: d.finding.category, file: d.finding.file }, story.acceptanceCriteria, diffFiles)
|
|
43257
|
-
}));
|
|
43258
|
-
const adversarialAcceptAnalysis = blockingFindings.map((f) => ({
|
|
43259
|
-
finding: {
|
|
43260
|
-
file: f.file,
|
|
43261
|
-
line: f.line,
|
|
43262
|
-
severity: f.severity,
|
|
43263
|
-
category: f.category
|
|
43264
|
-
},
|
|
43265
|
-
acIndex: f.acIndex,
|
|
43266
|
-
rawCategory: f.category,
|
|
43267
|
-
counterfactual: analyzeStructuralCounterfactual({ acIndex: f.acIndex, category: f.category, file: f.file }, story.acceptanceCriteria, diffFiles)
|
|
43268
|
-
}));
|
|
43563
|
+
const { adversarialDropAnalysis, adversarialAcceptAnalysis } = buildCounterfactualTelemetry({
|
|
43564
|
+
acDropped,
|
|
43565
|
+
blockingFindings,
|
|
43566
|
+
acceptanceCriteria: story.acceptanceCriteria,
|
|
43567
|
+
diffFiles
|
|
43568
|
+
});
|
|
43269
43569
|
if (advisoryFindings.length > 0) {
|
|
43270
43570
|
logger?.debug("review", `Adversarial review: ${advisoryFindings.length} advisory findings (below threshold '${threshold}')`, {
|
|
43271
43571
|
storyId: story.id,
|
|
@@ -43307,7 +43607,8 @@ async function runAdversarialReview(opts) {
|
|
|
43307
43607
|
advisoryFindings: advisoryFindings.length > 0 ? advisoryReviewFindings : undefined,
|
|
43308
43608
|
diffAvailable,
|
|
43309
43609
|
adversarialDropAnalysis,
|
|
43310
|
-
adversarialAcceptAnalysis
|
|
43610
|
+
adversarialAcceptAnalysis,
|
|
43611
|
+
acks
|
|
43311
43612
|
});
|
|
43312
43613
|
const output = blockingFindings.length > 0 ? `Adversarial review failed:
|
|
43313
43614
|
|
|
@@ -43341,6 +43642,7 @@ ${formatFindings(blockingFindings)}` : "Adversarial review failed (no findings)"
|
|
|
43341
43642
|
storyId: story.id,
|
|
43342
43643
|
featureName,
|
|
43343
43644
|
parsed: true,
|
|
43645
|
+
acks,
|
|
43344
43646
|
failOpen: false,
|
|
43345
43647
|
passed: true,
|
|
43346
43648
|
passReason: "ac_quote_not_substring_demoted",
|
|
@@ -43378,6 +43680,7 @@ ${formatFindings(blockingFindings)}` : "Adversarial review failed (no findings)"
|
|
|
43378
43680
|
storyId: story.id,
|
|
43379
43681
|
featureName,
|
|
43380
43682
|
parsed: true,
|
|
43683
|
+
acks,
|
|
43381
43684
|
failOpen: false,
|
|
43382
43685
|
passed: false,
|
|
43383
43686
|
blockingThreshold: threshold,
|
|
@@ -43408,6 +43711,7 @@ ${dropSummary}`,
|
|
|
43408
43711
|
storyId: story.id,
|
|
43409
43712
|
featureName,
|
|
43410
43713
|
parsed: true,
|
|
43714
|
+
acks,
|
|
43411
43715
|
failOpen: false,
|
|
43412
43716
|
passed: true,
|
|
43413
43717
|
blockingThreshold: threshold,
|
|
@@ -43438,7 +43742,7 @@ var init_adversarial = __esm(() => {
|
|
|
43438
43742
|
init_logger2();
|
|
43439
43743
|
init_adversarial_review();
|
|
43440
43744
|
init_call();
|
|
43441
|
-
|
|
43745
|
+
init_adversarial_counterfactual_telemetry();
|
|
43442
43746
|
init_adversarial_helpers();
|
|
43443
43747
|
init_diff_utils();
|
|
43444
43748
|
init_finding_projection();
|
|
@@ -43898,7 +44202,8 @@ function recordSemanticDebateAudit(opts) {
|
|
|
43898
44202
|
passed: opts.passed,
|
|
43899
44203
|
blockingThreshold: opts.blockingThreshold,
|
|
43900
44204
|
result: opts.result,
|
|
43901
|
-
advisoryFindings: opts.advisoryFindings
|
|
44205
|
+
advisoryFindings: opts.advisoryFindings,
|
|
44206
|
+
acks: opts.acks
|
|
43902
44207
|
});
|
|
43903
44208
|
}
|
|
43904
44209
|
async function runSemanticDebate(opts) {
|
|
@@ -43952,12 +44257,16 @@ async function runSemanticDebate(opts) {
|
|
|
43952
44257
|
const debateCost = debateResult.totalCostUsd ?? 0;
|
|
43953
44258
|
const resolverPassed = debateResult.outcome === "passed";
|
|
43954
44259
|
const allFindings = [];
|
|
44260
|
+
const acks = [];
|
|
43955
44261
|
for (const p of debateResult.proposals) {
|
|
43956
44262
|
const parsed = parseLLMResponse(p.output);
|
|
43957
44263
|
if (parsed) {
|
|
43958
44264
|
allFindings.push(...parsed.findings);
|
|
44265
|
+
if (parsed.acks)
|
|
44266
|
+
acks.push(...parsed.acks.slice(0, MAX_ACKS - acks.length));
|
|
43959
44267
|
}
|
|
43960
44268
|
}
|
|
44269
|
+
const debateAcks = acks.length > 0 ? acks : undefined;
|
|
43961
44270
|
const seen = new Set;
|
|
43962
44271
|
const deduped = [];
|
|
43963
44272
|
for (const f of allFindings) {
|
|
@@ -43991,6 +44300,7 @@ async function runSemanticDebate(opts) {
|
|
|
43991
44300
|
storyId: story.id,
|
|
43992
44301
|
featureName,
|
|
43993
44302
|
parsed: true,
|
|
44303
|
+
acks: debateAcks,
|
|
43994
44304
|
passed: false,
|
|
43995
44305
|
blockingThreshold: debateThreshold,
|
|
43996
44306
|
result: {
|
|
@@ -44023,6 +44333,7 @@ ${formatFindings2(debateBlocking)}`,
|
|
|
44023
44333
|
storyId: story.id,
|
|
44024
44334
|
featureName,
|
|
44025
44335
|
parsed: true,
|
|
44336
|
+
acks: debateAcks,
|
|
44026
44337
|
passed: true,
|
|
44027
44338
|
blockingThreshold: debateThreshold,
|
|
44028
44339
|
result: {
|
|
@@ -44049,6 +44360,7 @@ ${formatFindings2(debateBlocking)}`,
|
|
|
44049
44360
|
storyId: story.id,
|
|
44050
44361
|
featureName,
|
|
44051
44362
|
parsed: true,
|
|
44363
|
+
acks: debateAcks,
|
|
44052
44364
|
passed: true,
|
|
44053
44365
|
blockingThreshold: debateThreshold,
|
|
44054
44366
|
result: {
|
|
@@ -44092,7 +44404,8 @@ function recordSemanticAudit(opts) {
|
|
|
44092
44404
|
passed: opts.passed,
|
|
44093
44405
|
blockingThreshold: opts.blockingThreshold,
|
|
44094
44406
|
result: opts.result,
|
|
44095
|
-
advisoryFindings: opts.advisoryFindings
|
|
44407
|
+
advisoryFindings: opts.advisoryFindings,
|
|
44408
|
+
acks: opts.acks
|
|
44096
44409
|
});
|
|
44097
44410
|
}
|
|
44098
44411
|
async function runSemanticReview(opts) {
|
|
@@ -44370,6 +44683,7 @@ async function runSemanticReview(opts) {
|
|
|
44370
44683
|
}
|
|
44371
44684
|
const threshold = blockingThreshold ?? "error";
|
|
44372
44685
|
const allFindings = opResult.findings;
|
|
44686
|
+
const acks = opResult.acks;
|
|
44373
44687
|
const blockingFindings = allFindings.filter((f) => isBlockingSeverity(f.severity, threshold));
|
|
44374
44688
|
const advisoryFindings = allFindings.filter((f) => !isBlockingSeverity(f.severity, threshold));
|
|
44375
44689
|
if (advisoryFindings.length > 0) {
|
|
@@ -44407,6 +44721,7 @@ ${formatFindings2(blockingFindings)}`;
|
|
|
44407
44721
|
failOpen: false,
|
|
44408
44722
|
passed: false,
|
|
44409
44723
|
blockingThreshold: threshold,
|
|
44724
|
+
acks,
|
|
44410
44725
|
result: {
|
|
44411
44726
|
passed: false,
|
|
44412
44727
|
findings: llmFindingsToReviewFindings(allFindings, { source: "semantic-review", isTestFile: testFileMatch })
|
|
@@ -44437,6 +44752,7 @@ ${formatFindings2(blockingFindings)}`;
|
|
|
44437
44752
|
storyId: story.id,
|
|
44438
44753
|
featureName,
|
|
44439
44754
|
parsed: true,
|
|
44755
|
+
acks,
|
|
44440
44756
|
failOpen: false,
|
|
44441
44757
|
passed: false,
|
|
44442
44758
|
blockingThreshold: threshold,
|
|
@@ -44462,6 +44778,7 @@ ${formatFindings2(blockingFindings)}`;
|
|
|
44462
44778
|
storyId: story.id,
|
|
44463
44779
|
featureName,
|
|
44464
44780
|
parsed: true,
|
|
44781
|
+
acks,
|
|
44465
44782
|
failOpen: false,
|
|
44466
44783
|
passed: true,
|
|
44467
44784
|
blockingThreshold: threshold,
|
|
@@ -44840,6 +45157,7 @@ var init_review = __esm(() => {
|
|
|
44840
45157
|
init_runner2();
|
|
44841
45158
|
init_requote_response();
|
|
44842
45159
|
init_severity();
|
|
45160
|
+
init_semantic_categories();
|
|
44843
45161
|
});
|
|
44844
45162
|
|
|
44845
45163
|
// src/prompts/builders/rectifier-builder-helpers.ts
|
|
@@ -46857,6 +47175,7 @@ function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
|
46857
47175
|
workdir,
|
|
46858
47176
|
pipelineStage: stage,
|
|
46859
47177
|
modelDef,
|
|
47178
|
+
...resolvedRunOptions.modelDef !== undefined ? {} : { modelTier: effectiveTier },
|
|
46860
47179
|
timeoutSeconds: resolvedRunOptions.timeoutSeconds ?? config2.execution?.sessionTimeoutSeconds ?? DEFAULT_CONFIG.execution.sessionTimeoutSeconds,
|
|
46861
47180
|
featureName,
|
|
46862
47181
|
storyId: story.id,
|
|
@@ -46864,6 +47183,7 @@ function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
|
46864
47183
|
});
|
|
46865
47184
|
}
|
|
46866
47185
|
} else {
|
|
47186
|
+
const pinned = hopKind.kind === "primary" && resolvedRunOptions.modelDef !== undefined;
|
|
46867
47187
|
const modelDef = hopKind.kind === "primary" ? resolvedRunOptions.modelDef ?? resolveModelForAgent(config2.models, agentName, effectiveTier, defaultAgent) : resolveModelForAgent(config2.models, agentName, effectiveTier, defaultAgent);
|
|
46868
47188
|
handle = await sessionManager.openSession(sessionName, {
|
|
46869
47189
|
agentName,
|
|
@@ -46871,6 +47191,7 @@ function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
|
46871
47191
|
workdir,
|
|
46872
47192
|
pipelineStage: stage,
|
|
46873
47193
|
modelDef,
|
|
47194
|
+
...pinned ? {} : { modelTier: effectiveTier },
|
|
46874
47195
|
timeoutSeconds: resolvedRunOptions.timeoutSeconds ?? config2.execution?.sessionTimeoutSeconds ?? DEFAULT_CONFIG.execution.sessionTimeoutSeconds,
|
|
46875
47196
|
featureName,
|
|
46876
47197
|
storyId: story.id,
|
|
@@ -47033,6 +47354,7 @@ async function callOp(ctx, op, input) {
|
|
|
47033
47354
|
const sessionName = sessionRole2 && ctx.packageDir ? computeAcpHandle(ctx.packageDir, ctx.featureName, ctx.storyId, sessionRole2) : undefined;
|
|
47034
47355
|
const completeOptions = {
|
|
47035
47356
|
modelDef: resolved.modelDef,
|
|
47357
|
+
...resolved.modelTier !== undefined ? { modelTier: resolved.modelTier } : {},
|
|
47036
47358
|
jsonMode: completeOp.jsonMode ?? false,
|
|
47037
47359
|
pipelineStage: op.stage,
|
|
47038
47360
|
storyId: ctx.storyId,
|
|
@@ -48088,7 +48410,7 @@ var init_agent_stream_logging = __esm(() => {
|
|
|
48088
48410
|
});
|
|
48089
48411
|
|
|
48090
48412
|
// src/runtime/middleware/cost.ts
|
|
48091
|
-
function attachCostSubscriber(bus, aggregator, runId) {
|
|
48413
|
+
function attachCostSubscriber(bus, aggregator, runId, projectKey) {
|
|
48092
48414
|
const offDispatch = bus.onDispatch((event) => {
|
|
48093
48415
|
const tu = event.tokenUsage;
|
|
48094
48416
|
const wireExactCostUsd = event.exactCostUsd;
|
|
@@ -48101,9 +48423,15 @@ function attachCostSubscriber(bus, aggregator, runId) {
|
|
|
48101
48423
|
const costEvent = {
|
|
48102
48424
|
ts: event.timestamp,
|
|
48103
48425
|
runId,
|
|
48426
|
+
...projectKey !== undefined ? { projectKey } : {},
|
|
48427
|
+
schemaVersion: COST_ROW_SCHEMA_VERSION,
|
|
48104
48428
|
agentName: event.agentName,
|
|
48105
|
-
model: "unknown",
|
|
48429
|
+
model: event.model ?? "unknown",
|
|
48430
|
+
...event.modelTier !== undefined ? { modelTier: event.modelTier } : {},
|
|
48431
|
+
...event.profile !== undefined ? { profile: event.profile } : {},
|
|
48106
48432
|
stage: event.stage,
|
|
48433
|
+
sessionRole: event.sessionRole,
|
|
48434
|
+
...event.featureName !== undefined ? { featureName: event.featureName } : {},
|
|
48107
48435
|
storyId: event.storyId,
|
|
48108
48436
|
callId: event.callId,
|
|
48109
48437
|
scopeId: event.scopeId,
|
|
@@ -48117,14 +48445,18 @@ function attachCostSubscriber(bus, aggregator, runId) {
|
|
|
48117
48445
|
exactCostUsd,
|
|
48118
48446
|
costUsd: exactCostUsd,
|
|
48119
48447
|
confidence,
|
|
48448
|
+
pricingSource: hasWireExactCost ? "wire" : resolvePricingSource(event.model),
|
|
48120
48449
|
durationMs: event.durationMs
|
|
48121
48450
|
};
|
|
48122
48451
|
aggregator.record(costEvent);
|
|
48123
48452
|
});
|
|
48124
48453
|
const offError = bus.onDispatchError((event) => {
|
|
48125
48454
|
const errorEvent = {
|
|
48455
|
+
kind: "error",
|
|
48126
48456
|
ts: event.timestamp,
|
|
48127
48457
|
runId,
|
|
48458
|
+
...projectKey !== undefined ? { projectKey } : {},
|
|
48459
|
+
schemaVersion: COST_ROW_SCHEMA_VERSION,
|
|
48128
48460
|
agentName: event.agentName,
|
|
48129
48461
|
stage: event.stage,
|
|
48130
48462
|
storyId: event.storyId,
|
|
@@ -48153,6 +48485,10 @@ function attachCostSubscriber(bus, aggregator, runId) {
|
|
|
48153
48485
|
offCompleted();
|
|
48154
48486
|
};
|
|
48155
48487
|
}
|
|
48488
|
+
var COST_ROW_SCHEMA_VERSION = 2;
|
|
48489
|
+
var init_cost2 = __esm(() => {
|
|
48490
|
+
init_agents();
|
|
48491
|
+
});
|
|
48156
48492
|
|
|
48157
48493
|
// src/runtime/middleware/audit.ts
|
|
48158
48494
|
function attachAuditSubscriber(bus, auditor, runId) {
|
|
@@ -48251,6 +48587,7 @@ function attachReviewAuditSubscriber(bus, auditor, runId) {
|
|
|
48251
48587
|
blockingThreshold: event.blockingThreshold,
|
|
48252
48588
|
result: event.result,
|
|
48253
48589
|
advisoryFindings: event.advisoryFindings,
|
|
48590
|
+
acks: event.acks ? [...event.acks] : undefined,
|
|
48254
48591
|
acDropped: event.acDropped ? [...event.acDropped] : undefined,
|
|
48255
48592
|
unparsedPreview: event.unparsedPreview,
|
|
48256
48593
|
diffAvailable: event.diffAvailable,
|
|
@@ -48268,7 +48605,7 @@ function attachReviewAuditSubscriber(bus, auditor, runId) {
|
|
|
48268
48605
|
function scheduleTickIfNeeded(tickRef, tick, intervalMs) {
|
|
48269
48606
|
if (tickRef.handle !== null)
|
|
48270
48607
|
return;
|
|
48271
|
-
tickRef.handle = setTimeout(tick, intervalMs);
|
|
48608
|
+
tickRef.handle = _idleWatchdogDeps.setTimeout(tick, intervalMs);
|
|
48272
48609
|
}
|
|
48273
48610
|
function handleObserveTimeout(state, reason, idleDurationMs, nonToolCallIdleMs) {
|
|
48274
48611
|
if (state.warnedForCurrentIdlePeriod)
|
|
@@ -48296,7 +48633,7 @@ async function handleCancelTimeout(state, reason, controllerRegistry, maxRetryAt
|
|
|
48296
48633
|
return;
|
|
48297
48634
|
}
|
|
48298
48635
|
state.cancelAttempts++;
|
|
48299
|
-
state.lastActivityAt =
|
|
48636
|
+
state.lastActivityAt = _idleWatchdogDeps.now();
|
|
48300
48637
|
getSafeLogger()?.warn("idle-watchdog", reason === "tool_call_only_idle_timeout_exceeded" ? "Canceling tool-call-only idle call" : "Canceling idle call", {
|
|
48301
48638
|
storyId: state.storyId,
|
|
48302
48639
|
key: reason,
|
|
@@ -48330,17 +48667,17 @@ function handleWarnThenCancelTimeout(state, reason, controllerRegistry, maxRetry
|
|
|
48330
48667
|
});
|
|
48331
48668
|
state.inGracePeriod = true;
|
|
48332
48669
|
state.graceReason = reason;
|
|
48333
|
-
state.graceTimer = setTimeout(async () => {
|
|
48670
|
+
state.graceTimer = _idleWatchdogDeps.setTimeout(async () => {
|
|
48334
48671
|
if (!activeStates.has(state.callId))
|
|
48335
48672
|
return;
|
|
48336
48673
|
state.inGracePeriod = false;
|
|
48337
48674
|
state.graceTimer = undefined;
|
|
48338
48675
|
state.graceReason = undefined;
|
|
48339
|
-
const currentReason = getTimeoutReason(state,
|
|
48676
|
+
const currentReason = getTimeoutReason(state, _idleWatchdogDeps.now(), idleTimeoutMs, toolCallOnlyTimeoutMs);
|
|
48340
48677
|
if (currentReason !== reason)
|
|
48341
48678
|
return;
|
|
48342
48679
|
state.cancelAttempts++;
|
|
48343
|
-
state.lastActivityAt =
|
|
48680
|
+
state.lastActivityAt = _idleWatchdogDeps.now();
|
|
48344
48681
|
const cancel = controllerRegistry.get(state.callId);
|
|
48345
48682
|
if (cancel)
|
|
48346
48683
|
await cancel().catch(() => {});
|
|
@@ -48348,7 +48685,7 @@ function handleWarnThenCancelTimeout(state, reason, controllerRegistry, maxRetry
|
|
|
48348
48685
|
}
|
|
48349
48686
|
function clearGrace(state) {
|
|
48350
48687
|
if (state.inGracePeriod && state.graceTimer !== undefined) {
|
|
48351
|
-
clearTimeout(state.graceTimer);
|
|
48688
|
+
_idleWatchdogDeps.clearTimeout(state.graceTimer);
|
|
48352
48689
|
state.graceTimer = undefined;
|
|
48353
48690
|
state.inGracePeriod = false;
|
|
48354
48691
|
state.graceReason = undefined;
|
|
@@ -48384,7 +48721,7 @@ function attachAgentIdleWatchdog(agentStreamEvents, controllerRegistry, config2)
|
|
|
48384
48721
|
const tickRef = { handle: null };
|
|
48385
48722
|
function tick() {
|
|
48386
48723
|
tickRef.handle = null;
|
|
48387
|
-
const now =
|
|
48724
|
+
const now = _idleWatchdogDeps.now();
|
|
48388
48725
|
for (const [, state] of activeStates) {
|
|
48389
48726
|
if (state.inGracePeriod)
|
|
48390
48727
|
continue;
|
|
@@ -48407,7 +48744,7 @@ function attachAgentIdleWatchdog(agentStreamEvents, controllerRegistry, config2)
|
|
|
48407
48744
|
const unsubscribe = agentStreamEvents.onAgentStream((event) => {
|
|
48408
48745
|
switch (event.kind) {
|
|
48409
48746
|
case "agent.call_started": {
|
|
48410
|
-
const now =
|
|
48747
|
+
const now = _idleWatchdogDeps.now();
|
|
48411
48748
|
activeStates.set(event.callId, {
|
|
48412
48749
|
callId: event.callId,
|
|
48413
48750
|
agentName: event.agentName,
|
|
@@ -48479,11 +48816,11 @@ function attachAgentIdleWatchdog(agentStreamEvents, controllerRegistry, config2)
|
|
|
48479
48816
|
const state = activeStates.get(event.callId);
|
|
48480
48817
|
if (state) {
|
|
48481
48818
|
if (state.graceTimer !== undefined)
|
|
48482
|
-
clearTimeout(state.graceTimer);
|
|
48819
|
+
_idleWatchdogDeps.clearTimeout(state.graceTimer);
|
|
48483
48820
|
activeStates.delete(event.callId);
|
|
48484
48821
|
}
|
|
48485
48822
|
if (activeStates.size === 0 && tickRef.handle !== null) {
|
|
48486
|
-
clearTimeout(tickRef.handle);
|
|
48823
|
+
_idleWatchdogDeps.clearTimeout(tickRef.handle);
|
|
48487
48824
|
tickRef.handle = null;
|
|
48488
48825
|
}
|
|
48489
48826
|
break;
|
|
@@ -48494,17 +48831,23 @@ function attachAgentIdleWatchdog(agentStreamEvents, controllerRegistry, config2)
|
|
|
48494
48831
|
unsubscribe();
|
|
48495
48832
|
for (const state of activeStates.values()) {
|
|
48496
48833
|
if (state.graceTimer !== undefined)
|
|
48497
|
-
clearTimeout(state.graceTimer);
|
|
48834
|
+
_idleWatchdogDeps.clearTimeout(state.graceTimer);
|
|
48498
48835
|
}
|
|
48499
48836
|
activeStates.clear();
|
|
48500
48837
|
if (tickRef.handle !== null) {
|
|
48501
|
-
clearTimeout(tickRef.handle);
|
|
48838
|
+
_idleWatchdogDeps.clearTimeout(tickRef.handle);
|
|
48502
48839
|
tickRef.handle = null;
|
|
48503
48840
|
}
|
|
48504
48841
|
};
|
|
48505
48842
|
}
|
|
48843
|
+
var _idleWatchdogDeps;
|
|
48506
48844
|
var init_idle_watchdog = __esm(() => {
|
|
48507
48845
|
init_logger2();
|
|
48846
|
+
_idleWatchdogDeps = {
|
|
48847
|
+
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
48848
|
+
clearTimeout: (id) => clearTimeout(id),
|
|
48849
|
+
now: () => Date.now()
|
|
48850
|
+
};
|
|
48508
48851
|
});
|
|
48509
48852
|
|
|
48510
48853
|
// src/runtime/middleware/index.ts
|
|
@@ -48512,6 +48855,7 @@ var init_middleware = __esm(() => {
|
|
|
48512
48855
|
init_cancellation();
|
|
48513
48856
|
init_logging();
|
|
48514
48857
|
init_agent_stream_logging();
|
|
48858
|
+
init_cost2();
|
|
48515
48859
|
init_idle_watchdog();
|
|
48516
48860
|
});
|
|
48517
48861
|
|
|
@@ -49069,6 +49413,11 @@ var init_manager_sweep = __esm(() => {
|
|
|
49069
49413
|
DEFAULT_ORPHAN_TTL_MS = 4 * 60 * 60 * 1000;
|
|
49070
49414
|
});
|
|
49071
49415
|
|
|
49416
|
+
// src/session/model-selection.ts
|
|
49417
|
+
function selectModel(opts) {
|
|
49418
|
+
return { modelDef: opts.modelDef, ...opts.modelTier ? { modelTier: opts.modelTier } : {} };
|
|
49419
|
+
}
|
|
49420
|
+
|
|
49072
49421
|
// src/session/naming.ts
|
|
49073
49422
|
var exports_naming = {};
|
|
49074
49423
|
__export(exports_naming, {
|
|
@@ -49374,7 +49723,7 @@ class SessionManager {
|
|
|
49374
49723
|
agentName: opts.agentName,
|
|
49375
49724
|
workdir: opts.workdir,
|
|
49376
49725
|
resolvedPermissions,
|
|
49377
|
-
|
|
49726
|
+
...selectModel(opts),
|
|
49378
49727
|
timeoutSeconds: opts.timeoutSeconds,
|
|
49379
49728
|
onPidSpawned: this._pidRegistry ? (pid) => this._pidRegistry?.register(pid) : undefined,
|
|
49380
49729
|
onPidExited: this._pidRegistry ? (pid) => this._pidRegistry?.unregister(pid) : undefined,
|
|
@@ -49649,8 +49998,8 @@ async function triageFlakyFindings(input) {
|
|
|
49649
49998
|
result.push({ ...f });
|
|
49650
49999
|
return { findings: result, quarantineReport: { keys, reasons } };
|
|
49651
50000
|
}
|
|
49652
|
-
const changedTestSet = new Set(diff.changedTestFiles.map(
|
|
49653
|
-
const mappedTestSet = new Set(diff.mappedTestFiles.map(
|
|
50001
|
+
const changedTestSet = new Set(diff.changedTestFiles.map(basename6));
|
|
50002
|
+
const mappedTestSet = new Set(diff.mappedTestFiles.map(basename6));
|
|
49654
50003
|
const candidates = findings.filter((f) => isProbeCandidate(f, changedTestSet, mappedTestSet));
|
|
49655
50004
|
if (candidates.length > flakeDetection.maxProbesPerGate) {
|
|
49656
50005
|
logger?.info("flake-triage", `Skipping flake triage \u2014 ${candidates.length} candidates exceed maxProbesPerGate=${flakeDetection.maxProbesPerGate}`);
|
|
@@ -49714,7 +50063,7 @@ async function triageFlakyFindings(input) {
|
|
|
49714
50063
|
}
|
|
49715
50064
|
return { findings: result, quarantineReport: { keys, reasons } };
|
|
49716
50065
|
}
|
|
49717
|
-
function
|
|
50066
|
+
function basename6(path7) {
|
|
49718
50067
|
const i = path7.lastIndexOf("/");
|
|
49719
50068
|
return i === -1 ? path7 : path7.slice(i + 1);
|
|
49720
50069
|
}
|
|
@@ -49725,7 +50074,7 @@ function isProbeCandidate(finding, changedTestSet, mappedTestSet) {
|
|
|
49725
50074
|
return false;
|
|
49726
50075
|
if (!finding.rule)
|
|
49727
50076
|
return false;
|
|
49728
|
-
const base =
|
|
50077
|
+
const base = basename6(finding.file);
|
|
49729
50078
|
if (changedTestSet.has(base) || changedTestSet.has(finding.file))
|
|
49730
50079
|
return false;
|
|
49731
50080
|
if (mappedTestSet.has(base) || mappedTestSet.has(finding.file))
|
|
@@ -49859,6 +50208,7 @@ __export(exports_runtime, {
|
|
|
49859
50208
|
attachAgentIdleWatchdog: () => attachAgentIdleWatchdog,
|
|
49860
50209
|
_reviewAuditDeps: () => _reviewAuditDeps,
|
|
49861
50210
|
_promptAuditorDeps: () => _promptAuditorDeps,
|
|
50211
|
+
_idleWatchdogDeps: () => _idleWatchdogDeps,
|
|
49862
50212
|
_costAggDeps: () => _costAggDeps,
|
|
49863
50213
|
ReviewAuditor: () => ReviewAuditor,
|
|
49864
50214
|
PromptAuditor: () => PromptAuditor,
|
|
@@ -49868,7 +50218,7 @@ __export(exports_runtime, {
|
|
|
49868
50218
|
CostAggregator: () => CostAggregator,
|
|
49869
50219
|
AgentStreamEventBus: () => AgentStreamEventBus
|
|
49870
50220
|
});
|
|
49871
|
-
import { basename as
|
|
50221
|
+
import { basename as basename7, join as join31 } from "path";
|
|
49872
50222
|
function createRuntime(config2, workdir, opts) {
|
|
49873
50223
|
const runId = crypto.randomUUID();
|
|
49874
50224
|
const controller = new AbortController;
|
|
@@ -49880,7 +50230,7 @@ function createRuntime(config2, workdir, opts) {
|
|
|
49880
50230
|
const configLoader = createConfigLoader(config2);
|
|
49881
50231
|
const dispatchEvents = new DispatchEventBus;
|
|
49882
50232
|
const agentStreamEvents = opts?.agentStreamEvents ?? new AgentStreamEventBus;
|
|
49883
|
-
const projectKey = config2.name?.trim() ||
|
|
50233
|
+
const projectKey = config2.name?.trim() || basename7(workdir);
|
|
49884
50234
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
49885
50235
|
const globalDir = globalOutputDir();
|
|
49886
50236
|
const curatorRollupPathValue = curatorRollupPath(globalDir, config2.curator?.rollupPath);
|
|
@@ -49935,7 +50285,7 @@ function createRuntime(config2, workdir, opts) {
|
|
|
49935
50285
|
agentManager.configureRuntime({ pidRegistry });
|
|
49936
50286
|
}
|
|
49937
50287
|
const offLogging = attachLoggingSubscriber(dispatchEvents, runId);
|
|
49938
|
-
const offCost = attachCostSubscriber(dispatchEvents, costAggregator, runId);
|
|
50288
|
+
const offCost = attachCostSubscriber(dispatchEvents, costAggregator, runId, getProjectKey(config2, workdir));
|
|
49939
50289
|
const offAudit = attachAuditSubscriber(dispatchEvents, promptAuditor, runId);
|
|
49940
50290
|
const offReviewAudit = attachReviewAuditSubscriber(dispatchEvents, reviewAuditor, runId);
|
|
49941
50291
|
const offAgentStreamLogging = attachAgentStreamLogging(agentStreamEvents, runId);
|
|
@@ -52864,7 +53214,8 @@ var init_telegram = __esm(() => {
|
|
|
52864
53214
|
init_zod();
|
|
52865
53215
|
init_logger2();
|
|
52866
53216
|
_telegramPluginDeps = {
|
|
52867
|
-
fetch: globalThis.fetch.bind(globalThis)
|
|
53217
|
+
fetch: globalThis.fetch.bind(globalThis),
|
|
53218
|
+
basePollBackoffMs: 1000
|
|
52868
53219
|
};
|
|
52869
53220
|
NUMERIC_CHAT_ID = /^-?\d+$/;
|
|
52870
53221
|
TelegramConfigSchema = exports_external.object({
|
|
@@ -52878,7 +53229,7 @@ var init_telegram = __esm(() => {
|
|
|
52878
53229
|
chatId = null;
|
|
52879
53230
|
pendingMessages = new Map;
|
|
52880
53231
|
lastUpdateId = 0;
|
|
52881
|
-
backoffMs =
|
|
53232
|
+
backoffMs = _telegramPluginDeps.basePollBackoffMs;
|
|
52882
53233
|
maxBackoffMs = 30000;
|
|
52883
53234
|
static MAX_DRAIN_PAGES = 10;
|
|
52884
53235
|
static INTERACTIVE_REQUEST_TYPES = new Set([
|
|
@@ -52988,7 +53339,7 @@ ${partLabel}${chunks[i]}`;
|
|
|
52988
53339
|
this.clearInlineKeyboard(update.callback_query.message.message_id);
|
|
52989
53340
|
}
|
|
52990
53341
|
this.pendingMessages.delete(requestId);
|
|
52991
|
-
this.backoffMs =
|
|
53342
|
+
this.backoffMs = _telegramPluginDeps.basePollBackoffMs;
|
|
52992
53343
|
return response;
|
|
52993
53344
|
}
|
|
52994
53345
|
if (update.callback_query) {
|
|
@@ -53059,7 +53410,7 @@ ${partLabel}${chunks[i]}`;
|
|
|
53059
53410
|
rejected: raw.length - updates.length
|
|
53060
53411
|
});
|
|
53061
53412
|
}
|
|
53062
|
-
this.backoffMs =
|
|
53413
|
+
this.backoffMs = _telegramPluginDeps.basePollBackoffMs;
|
|
53063
53414
|
return { ok: true, updates, rawCount: raw.length };
|
|
53064
53415
|
} catch (err) {
|
|
53065
53416
|
this.backoffMs = Math.min(this.backoffMs * 2, this.maxBackoffMs);
|
|
@@ -55871,10 +56222,10 @@ __export(exports_status_cost, {
|
|
|
55871
56222
|
displayCostMetrics: () => displayCostMetrics,
|
|
55872
56223
|
_costReportEmitDeps: () => _costReportEmitDeps
|
|
55873
56224
|
});
|
|
55874
|
-
import { basename as
|
|
56225
|
+
import { basename as basename8 } from "path";
|
|
55875
56226
|
async function resolveProject(workdir) {
|
|
55876
56227
|
const config2 = await loadConfig(workdir).catch(() => null);
|
|
55877
|
-
const project = config2?.name?.trim() ||
|
|
56228
|
+
const project = config2?.name?.trim() || basename8(workdir);
|
|
55878
56229
|
const outputDir = projectOutputDir(project, config2?.outputDir);
|
|
55879
56230
|
return { project, outputDir };
|
|
55880
56231
|
}
|
|
@@ -56139,7 +56490,7 @@ __export(exports_status_features, {
|
|
|
56139
56490
|
_statusFeaturesDeps: () => _statusFeaturesDeps
|
|
56140
56491
|
});
|
|
56141
56492
|
import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
|
|
56142
|
-
import { basename as
|
|
56493
|
+
import { basename as basename9, join as join45, resolve as resolve14 } from "path";
|
|
56143
56494
|
function isPidAlive(pid) {
|
|
56144
56495
|
try {
|
|
56145
56496
|
process.kill(pid, 0);
|
|
@@ -56162,7 +56513,7 @@ async function loadStatusFile(featureDir) {
|
|
|
56162
56513
|
}
|
|
56163
56514
|
async function loadProjectStatusFile(projectDir) {
|
|
56164
56515
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
56165
|
-
const projectKey = config2?.name?.trim() ||
|
|
56516
|
+
const projectKey = config2?.name?.trim() || basename9(projectDir);
|
|
56166
56517
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
56167
56518
|
const statusPath = join45(outputDir, "status.json");
|
|
56168
56519
|
if (!existsSync17(statusPath)) {
|
|
@@ -56233,7 +56584,7 @@ async function getFeatureSummary(featureName, featureDir) {
|
|
|
56233
56584
|
}
|
|
56234
56585
|
async function displayAllFeatures(projectDir) {
|
|
56235
56586
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
56236
|
-
const projectKey = config2?.name?.trim() ||
|
|
56587
|
+
const projectKey = config2?.name?.trim() || basename9(projectDir);
|
|
56237
56588
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
56238
56589
|
const featuresDir = join45(outputDir, "features");
|
|
56239
56590
|
if (!existsSync17(featuresDir)) {
|
|
@@ -56456,7 +56807,7 @@ async function displayFeatureStatus(options = {}) {
|
|
|
56456
56807
|
if (options.dir) {
|
|
56457
56808
|
const projectDir = resolve14(options.dir);
|
|
56458
56809
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
56459
|
-
const projectKey = config2?.name?.trim() ||
|
|
56810
|
+
const projectKey = config2?.name?.trim() || basename9(projectDir);
|
|
56460
56811
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
56461
56812
|
featureDir = join45(outputDir, "features", options.feature);
|
|
56462
56813
|
} else {
|
|
@@ -57087,7 +57438,7 @@ var init_acceptance3 = __esm(() => {
|
|
|
57087
57438
|
const allOutputParts = [];
|
|
57088
57439
|
let anyError = false;
|
|
57089
57440
|
let errorExitCode = 0;
|
|
57090
|
-
let
|
|
57441
|
+
let hardeningPromoted = 0;
|
|
57091
57442
|
for (const { testPath, packageDir, testFramework, commandOverride } of testGroups) {
|
|
57092
57443
|
const testFile = Bun.file(testPath);
|
|
57093
57444
|
const exists = await testFile.exists();
|
|
@@ -57188,7 +57539,7 @@ ${stderr}`;
|
|
|
57188
57539
|
runtime: ctx.runtime,
|
|
57189
57540
|
abortSignal: ctx.abortSignal
|
|
57190
57541
|
});
|
|
57191
|
-
|
|
57542
|
+
hardeningPromoted = result.promoted.length;
|
|
57192
57543
|
} catch (err) {
|
|
57193
57544
|
logger.debug("acceptance", "Hardening pass failed (non-blocking)", {
|
|
57194
57545
|
storyId: ctx.story.id,
|
|
@@ -57201,7 +57552,8 @@ ${stderr}`;
|
|
|
57201
57552
|
packageDir: ctx.workdir,
|
|
57202
57553
|
passed: true,
|
|
57203
57554
|
failedACs: [],
|
|
57204
|
-
retries:
|
|
57555
|
+
retries: ctx.acceptanceRetries ?? 0,
|
|
57556
|
+
hardeningPromoted,
|
|
57205
57557
|
durationMs
|
|
57206
57558
|
});
|
|
57207
57559
|
return { action: "continue" };
|
|
@@ -57217,7 +57569,8 @@ ${stderr}`;
|
|
|
57217
57569
|
packageDir: ctx.workdir,
|
|
57218
57570
|
passed: false,
|
|
57219
57571
|
failedACs: allFailedACs,
|
|
57220
|
-
retries:
|
|
57572
|
+
retries: ctx.acceptanceRetries ?? 0,
|
|
57573
|
+
hardeningPromoted,
|
|
57221
57574
|
durationMs
|
|
57222
57575
|
});
|
|
57223
57576
|
if (anyError) {
|
|
@@ -62791,7 +63144,8 @@ var init_gitignore = __esm(() => {
|
|
|
62791
63144
|
"**/_nax_acceptance_test.py",
|
|
62792
63145
|
"**/_nax_suggested_test.py",
|
|
62793
63146
|
"**/.nax/features/*/",
|
|
62794
|
-
".nax/prompt-audit/"
|
|
63147
|
+
".nax/prompt-audit/",
|
|
63148
|
+
".nax/finish-audit/"
|
|
62795
63149
|
];
|
|
62796
63150
|
});
|
|
62797
63151
|
|
|
@@ -62804,7 +63158,7 @@ __export(exports_init_context, {
|
|
|
62804
63158
|
generatePackageContextTemplate: () => generatePackageContextTemplate,
|
|
62805
63159
|
generateContextTemplate: () => generateContextTemplate
|
|
62806
63160
|
});
|
|
62807
|
-
import { basename as
|
|
63161
|
+
import { basename as basename11, join as join54 } from "path";
|
|
62808
63162
|
async function bunFileExists(path16) {
|
|
62809
63163
|
return Bun.file(path16).exists();
|
|
62810
63164
|
}
|
|
@@ -62899,7 +63253,7 @@ async function scanProject(projectRoot) {
|
|
|
62899
63253
|
const readmeSnippet = await readReadmeSnippet(projectRoot);
|
|
62900
63254
|
const entryPoints = await detectEntryPoints(projectRoot);
|
|
62901
63255
|
const configFiles = await detectConfigFiles(projectRoot);
|
|
62902
|
-
const projectName = packageManifest?.name ||
|
|
63256
|
+
const projectName = packageManifest?.name || basename11(projectRoot);
|
|
62903
63257
|
return {
|
|
62904
63258
|
projectName,
|
|
62905
63259
|
fileTree,
|
|
@@ -64322,7 +64676,8 @@ async function collectFromMetrics(context) {
|
|
|
64322
64676
|
const success2 = boolValue(story.success, false);
|
|
64323
64677
|
const storyId = stringValue(story.storyId ?? story.id, "unknown");
|
|
64324
64678
|
const obs = {
|
|
64325
|
-
schemaVersion:
|
|
64679
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64680
|
+
projectKey: context.projectKey,
|
|
64326
64681
|
runId: context.runId,
|
|
64327
64682
|
featureId: stringValue(currentRun.feature, context.feature),
|
|
64328
64683
|
storyId,
|
|
@@ -64342,6 +64697,24 @@ async function collectFromMetrics(context) {
|
|
|
64342
64697
|
} catch {}
|
|
64343
64698
|
return observations;
|
|
64344
64699
|
}
|
|
64700
|
+
function withinRun(timestamp, runStartedAt) {
|
|
64701
|
+
if (runStartedAt === undefined)
|
|
64702
|
+
return true;
|
|
64703
|
+
const ts = typeof timestamp === "string" ? Date.parse(timestamp) : Number.NaN;
|
|
64704
|
+
if (Number.isNaN(ts))
|
|
64705
|
+
return true;
|
|
64706
|
+
return ts >= runStartedAt;
|
|
64707
|
+
}
|
|
64708
|
+
async function writtenThisRun(filePath, runStartedAt) {
|
|
64709
|
+
if (runStartedAt === undefined)
|
|
64710
|
+
return true;
|
|
64711
|
+
try {
|
|
64712
|
+
const stat = await Bun.file(filePath).stat();
|
|
64713
|
+
return stat.mtimeMs >= runStartedAt;
|
|
64714
|
+
} catch {
|
|
64715
|
+
return true;
|
|
64716
|
+
}
|
|
64717
|
+
}
|
|
64345
64718
|
function findingRuleId(finding) {
|
|
64346
64719
|
return stringValue(finding.ruleId ?? finding.rule ?? finding.checkId ?? finding.category, "unknown");
|
|
64347
64720
|
}
|
|
@@ -64367,17 +64740,22 @@ async function collectFromReviewAudit(context) {
|
|
|
64367
64740
|
const audit = asRecord3(await readJsonFile(fullPath));
|
|
64368
64741
|
if (!audit)
|
|
64369
64742
|
continue;
|
|
64743
|
+
const featureId = stringValue(audit.featureName ?? audit.featureId, context.feature);
|
|
64744
|
+
if (context.runStartedAt !== undefined && featureId !== context.feature)
|
|
64745
|
+
continue;
|
|
64746
|
+
if (!withinRun(audit.timestamp, context.runStartedAt))
|
|
64747
|
+
continue;
|
|
64370
64748
|
const result = asRecord3(audit.result);
|
|
64371
64749
|
const findings = asArray(result?.findings);
|
|
64372
64750
|
const storyId = stringValue(audit.storyId, "unknown");
|
|
64373
|
-
const featureId = stringValue(audit.featureName ?? audit.featureId, context.feature);
|
|
64374
64751
|
for (const rawFinding of findings) {
|
|
64375
64752
|
const finding = asRecord3(rawFinding);
|
|
64376
64753
|
if (!finding)
|
|
64377
64754
|
continue;
|
|
64378
64755
|
const ruleId = findingRuleId(finding);
|
|
64379
64756
|
const obs = {
|
|
64380
|
-
schemaVersion:
|
|
64757
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64758
|
+
projectKey: context.projectKey,
|
|
64381
64759
|
runId: context.runId,
|
|
64382
64760
|
featureId,
|
|
64383
64761
|
storyId,
|
|
@@ -64388,6 +64766,7 @@ async function collectFromReviewAudit(context) {
|
|
|
64388
64766
|
ruleId,
|
|
64389
64767
|
checkId: ruleId,
|
|
64390
64768
|
severity: stringValue(finding.severity, "info"),
|
|
64769
|
+
category: optionalString(finding.category),
|
|
64391
64770
|
file: stringValue(finding.file),
|
|
64392
64771
|
line: numberValue(finding.line, 0),
|
|
64393
64772
|
message: findingMessage(finding)
|
|
@@ -64403,6 +64782,7 @@ async function collectFromReviewAudit(context) {
|
|
|
64403
64782
|
async function collectFromContextManifests(context) {
|
|
64404
64783
|
const observations = [];
|
|
64405
64784
|
const featuresDir = path18.join(context.workdir, ".nax", "features");
|
|
64785
|
+
let skippedManifests = 0;
|
|
64406
64786
|
try {
|
|
64407
64787
|
const glob = new Bun.Glob("*/stories/*/context-manifest-*.json");
|
|
64408
64788
|
for await (const file3 of glob.scan({ cwd: featuresDir, absolute: false })) {
|
|
@@ -64411,15 +64791,21 @@ async function collectFromContextManifests(context) {
|
|
|
64411
64791
|
const parts = file3.split("/");
|
|
64412
64792
|
const featureId = parts[0] ?? context.feature;
|
|
64413
64793
|
const storyId = parts[2] ?? "unknown";
|
|
64794
|
+
if (!await writtenThisRun(fullPath, context.runStartedAt)) {
|
|
64795
|
+
skippedManifests += 1;
|
|
64796
|
+
continue;
|
|
64797
|
+
}
|
|
64414
64798
|
const manifest = asRecord3(await readJsonFile(fullPath));
|
|
64415
64799
|
if (!manifest)
|
|
64416
64800
|
continue;
|
|
64417
64801
|
const ts = now();
|
|
64418
64802
|
const chunkSummaries = asRecord3(manifest.chunkSummaries) ?? {};
|
|
64803
|
+
const chunkTokens = asRecord3(manifest.chunkTokens) ?? {};
|
|
64419
64804
|
for (const chunkId of asArray(manifest.includedChunks)) {
|
|
64420
64805
|
const id = String(chunkId);
|
|
64421
64806
|
const obs = {
|
|
64422
|
-
schemaVersion:
|
|
64807
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64808
|
+
projectKey: context.projectKey,
|
|
64423
64809
|
runId: context.runId,
|
|
64424
64810
|
featureId,
|
|
64425
64811
|
storyId,
|
|
@@ -64429,7 +64815,7 @@ async function collectFromContextManifests(context) {
|
|
|
64429
64815
|
payload: {
|
|
64430
64816
|
chunkId: id,
|
|
64431
64817
|
label: stringValue(chunkSummaries[id], id),
|
|
64432
|
-
tokens: 0
|
|
64818
|
+
tokens: numberValue(chunkTokens[id], 0)
|
|
64433
64819
|
}
|
|
64434
64820
|
};
|
|
64435
64821
|
observations.push(obs);
|
|
@@ -64440,7 +64826,8 @@ async function collectFromContextManifests(context) {
|
|
|
64440
64826
|
continue;
|
|
64441
64827
|
const id = stringValue(excluded.id, "unknown");
|
|
64442
64828
|
const obs = {
|
|
64443
|
-
schemaVersion:
|
|
64829
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64830
|
+
projectKey: context.projectKey,
|
|
64444
64831
|
runId: context.runId,
|
|
64445
64832
|
featureId,
|
|
64446
64833
|
storyId,
|
|
@@ -64460,7 +64847,8 @@ async function collectFromContextManifests(context) {
|
|
|
64460
64847
|
if (!provider || stringValue(provider.status) !== "empty")
|
|
64461
64848
|
continue;
|
|
64462
64849
|
const obs = {
|
|
64463
|
-
schemaVersion:
|
|
64850
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64851
|
+
projectKey: context.projectKey,
|
|
64464
64852
|
runId: context.runId,
|
|
64465
64853
|
featureId,
|
|
64466
64854
|
storyId,
|
|
@@ -64491,7 +64879,8 @@ function entryFeatureId(context, entry, data) {
|
|
|
64491
64879
|
function collectPullCall(context, entry, data) {
|
|
64492
64880
|
const toolName = stringValue(data.tool ?? data.toolName, "unknown");
|
|
64493
64881
|
return {
|
|
64494
|
-
schemaVersion:
|
|
64882
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64883
|
+
projectKey: context.projectKey,
|
|
64495
64884
|
runId: context.runId,
|
|
64496
64885
|
featureId: entryFeatureId(context, entry, data),
|
|
64497
64886
|
storyId: entryStoryId(entry, data),
|
|
@@ -64510,7 +64899,8 @@ function collectPullCall(context, entry, data) {
|
|
|
64510
64899
|
}
|
|
64511
64900
|
function collectAcceptanceVerdict(context, entry, data) {
|
|
64512
64901
|
return {
|
|
64513
|
-
schemaVersion:
|
|
64902
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64903
|
+
projectKey: context.projectKey,
|
|
64514
64904
|
runId: context.runId,
|
|
64515
64905
|
featureId: entryFeatureId(context, entry, data),
|
|
64516
64906
|
storyId: entryStoryId(entry, data),
|
|
@@ -64528,7 +64918,8 @@ function collectAcceptanceVerdict(context, entry, data) {
|
|
|
64528
64918
|
}
|
|
64529
64919
|
function collectRectify(context, entry, data) {
|
|
64530
64920
|
return {
|
|
64531
|
-
schemaVersion:
|
|
64921
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64922
|
+
projectKey: context.projectKey,
|
|
64532
64923
|
runId: context.runId,
|
|
64533
64924
|
featureId: entryFeatureId(context, entry, data),
|
|
64534
64925
|
storyId: entryStoryId(entry, data),
|
|
@@ -64543,7 +64934,8 @@ function collectRectify(context, entry, data) {
|
|
|
64543
64934
|
}
|
|
64544
64935
|
function collectEscalation(context, entry, data) {
|
|
64545
64936
|
return {
|
|
64546
|
-
schemaVersion:
|
|
64937
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64938
|
+
projectKey: context.projectKey,
|
|
64547
64939
|
runId: context.runId,
|
|
64548
64940
|
featureId: entryFeatureId(context, entry, data),
|
|
64549
64941
|
storyId: entryStoryId(entry, data),
|
|
@@ -64559,7 +64951,8 @@ function collectEscalation(context, entry, data) {
|
|
|
64559
64951
|
function collectFixCycleIteration(context, entry, data) {
|
|
64560
64952
|
const outcome = optionalString(data.outcome);
|
|
64561
64953
|
return {
|
|
64562
|
-
schemaVersion:
|
|
64954
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64955
|
+
projectKey: context.projectKey,
|
|
64563
64956
|
runId: context.runId,
|
|
64564
64957
|
featureId: entryFeatureId(context, entry, data),
|
|
64565
64958
|
storyId: entryStoryId(entry, data),
|
|
@@ -64579,7 +64972,8 @@ function collectFixCycleIteration(context, entry, data) {
|
|
|
64579
64972
|
}
|
|
64580
64973
|
function collectFixCycleExit(context, entry, data) {
|
|
64581
64974
|
return {
|
|
64582
|
-
schemaVersion:
|
|
64975
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64976
|
+
projectKey: context.projectKey,
|
|
64583
64977
|
runId: context.runId,
|
|
64584
64978
|
featureId: entryFeatureId(context, entry, data),
|
|
64585
64979
|
storyId: entryStoryId(entry, data),
|
|
@@ -64594,7 +64988,8 @@ function collectFixCycleExit(context, entry, data) {
|
|
|
64594
64988
|
}
|
|
64595
64989
|
function collectFixCycleRetry(context, entry, data) {
|
|
64596
64990
|
return {
|
|
64597
|
-
schemaVersion:
|
|
64991
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64992
|
+
projectKey: context.projectKey,
|
|
64598
64993
|
runId: context.runId,
|
|
64599
64994
|
featureId: entryFeatureId(context, entry, data),
|
|
64600
64995
|
storyId: entryStoryId(entry, data),
|
|
@@ -64648,6 +65043,7 @@ async function collectObservations(context) {
|
|
|
64648
65043
|
]);
|
|
64649
65044
|
return [...metricsObs, ...auditObs, ...manifestObs, ...jsonlObs];
|
|
64650
65045
|
}
|
|
65046
|
+
var OBSERVATION_SCHEMA_VERSION = 3;
|
|
64651
65047
|
var init_collect = () => {};
|
|
64652
65048
|
|
|
64653
65049
|
// src/plugins/builtin/curator/heuristics.ts
|
|
@@ -64664,6 +65060,12 @@ function mergeThresholds(thresholds) {
|
|
|
64664
65060
|
function uniqueStoryIds(storyIds) {
|
|
64665
65061
|
return [...new Set(storyIds)];
|
|
64666
65062
|
}
|
|
65063
|
+
function crossFeatureKey(category, message) {
|
|
65064
|
+
return `${category ?? ""}|${normalizeIssueText(message).slice(0, CROSS_FEATURE_MESSAGE_PREFIX)}`;
|
|
65065
|
+
}
|
|
65066
|
+
function truncate3(s, max) {
|
|
65067
|
+
return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
|
|
65068
|
+
}
|
|
64667
65069
|
function firstLine2(message) {
|
|
64668
65070
|
return message.split(`
|
|
64669
65071
|
`)[0] ?? message;
|
|
@@ -64672,37 +65074,45 @@ function h1RepeatedReviewFinding(observations, threshold) {
|
|
|
64672
65074
|
const findings = observations.filter((o) => o.kind === "review-finding");
|
|
64673
65075
|
const groups = new Map;
|
|
64674
65076
|
for (const obs of findings) {
|
|
64675
|
-
const
|
|
64676
|
-
|
|
64677
|
-
|
|
64678
|
-
|
|
64679
|
-
|
|
64680
|
-
|
|
64681
|
-
|
|
64682
|
-
|
|
64683
|
-
|
|
64684
|
-
} else {
|
|
64685
|
-
const sampleKey = firstLine2(message);
|
|
64686
|
-
groups.set(ruleId, { storyIds: [obs.storyId], samples: sampleKey ? [sampleKey] : [] });
|
|
65077
|
+
const { file: file3, category, message } = obs.payload;
|
|
65078
|
+
if (!file3 && !category)
|
|
65079
|
+
continue;
|
|
65080
|
+
const key = crossFeatureKey(category, message);
|
|
65081
|
+
let group = groups.get(key);
|
|
65082
|
+
if (!group) {
|
|
65083
|
+
group = { featureIds: new Set, sites: new Set, files: new Set, samples: [] };
|
|
65084
|
+
groups.set(key, group);
|
|
65085
|
+
group.category = category;
|
|
64687
65086
|
}
|
|
65087
|
+
group.featureIds.add(obs.featureId);
|
|
65088
|
+
group.sites.add(`${obs.featureId}/${obs.storyId}`);
|
|
65089
|
+
if (file3)
|
|
65090
|
+
group.files.add(file3);
|
|
65091
|
+
const sample = firstLine2(message);
|
|
65092
|
+
if (sample && group.samples.length < 2 && !group.samples.includes(sample))
|
|
65093
|
+
group.samples.push(sample);
|
|
64688
65094
|
}
|
|
64689
65095
|
const proposals = [];
|
|
64690
|
-
for (const
|
|
64691
|
-
|
|
65096
|
+
for (const group of groups.values()) {
|
|
65097
|
+
const featureCount = group.featureIds.size;
|
|
65098
|
+
if (featureCount < threshold)
|
|
64692
65099
|
continue;
|
|
64693
|
-
const
|
|
64694
|
-
const
|
|
64695
|
-
const
|
|
64696
|
-
const
|
|
64697
|
-
|
|
65100
|
+
const features = [...group.featureIds];
|
|
65101
|
+
const sites = [...group.sites];
|
|
65102
|
+
const files = [...group.files];
|
|
65103
|
+
const gist = group.samples[0] ? truncate3(group.samples[0], DESCRIPTION_GIST_CHARS) : "(no description)";
|
|
65104
|
+
const categoryLabel = group.category ? `${group.category}: ` : "";
|
|
65105
|
+
const fileSection = files.length > 0 ? ` Files: ${files.slice(0, MAX_EVIDENCE_FILES).join(", ")}.` : "";
|
|
65106
|
+
const sampleSection = group.samples.length > 0 ? `
|
|
65107
|
+
Examples: ${group.samples.join(" | ")}` : "";
|
|
64698
65108
|
proposals.push({
|
|
64699
65109
|
id: "H1",
|
|
64700
|
-
severity:
|
|
65110
|
+
severity: featureCount >= threshold * HIGH_SEVERITY_MULTIPLE ? "HIGH" : "MED",
|
|
64701
65111
|
target: { canonicalFile: ".nax/rules/curator-suggestions.md", action: "add" },
|
|
64702
|
-
description: `
|
|
64703
|
-
evidence: `
|
|
65112
|
+
description: `Recurring across ${featureCount} features \u2014 ${categoryLabel}${gist}`,
|
|
65113
|
+
evidence: `Seen in ${featureCount} features: ${features.join(", ")} (sites: ${sites.join(", ")}).${fileSection}${sampleSection}`,
|
|
64704
65114
|
sourceKinds: ["review-finding"],
|
|
64705
|
-
storyIds:
|
|
65115
|
+
storyIds: sites
|
|
64706
65116
|
});
|
|
64707
65117
|
}
|
|
64708
65118
|
return proposals;
|
|
@@ -64873,8 +65283,9 @@ function runHeuristics(observations, thresholds) {
|
|
|
64873
65283
|
...h6FixCycleUnchanged(observations, t.unchangedOutcome)
|
|
64874
65284
|
];
|
|
64875
65285
|
}
|
|
64876
|
-
var DEFAULT_THRESHOLDS2;
|
|
65286
|
+
var DEFAULT_THRESHOLDS2, HIGH_SEVERITY_MULTIPLE = 2, DESCRIPTION_GIST_CHARS = 90, MAX_EVIDENCE_FILES = 4, CROSS_FEATURE_MESSAGE_PREFIX = 48;
|
|
64877
65287
|
var init_heuristics = __esm(() => {
|
|
65288
|
+
init_review();
|
|
64878
65289
|
DEFAULT_THRESHOLDS2 = {
|
|
64879
65290
|
repeatedFinding: 2,
|
|
64880
65291
|
emptyKeyword: 2,
|
|
@@ -64947,7 +65358,7 @@ function renderProposals(proposals, runId, observationCount) {
|
|
|
64947
65358
|
const storyList = p.storyIds.join(", ");
|
|
64948
65359
|
lines.push(`- [ ] [${p.severity}] ${p.id}: ${p.description} \u2014 stories: ${storyList}`);
|
|
64949
65360
|
if (p.evidence) {
|
|
64950
|
-
lines.push(` _Evidence: ${p.evidence}_`);
|
|
65361
|
+
lines.push(` _Evidence: ${p.evidence.replace(/\s*\n\s*/g, " \xB7 ")}_`);
|
|
64951
65362
|
}
|
|
64952
65363
|
}
|
|
64953
65364
|
lines.push("");
|
|
@@ -64957,6 +65368,26 @@ function renderProposals(proposals, runId, observationCount) {
|
|
|
64957
65368
|
`);
|
|
64958
65369
|
}
|
|
64959
65370
|
|
|
65371
|
+
// src/plugins/builtin/curator/jsonl-stream.ts
|
|
65372
|
+
async function* streamJsonlLines(file3) {
|
|
65373
|
+
const decoder = new TextDecoder;
|
|
65374
|
+
let carry = "";
|
|
65375
|
+
for await (const chunk of file3.stream()) {
|
|
65376
|
+
carry += decoder.decode(chunk, { stream: true });
|
|
65377
|
+
let nl = carry.indexOf(`
|
|
65378
|
+
`);
|
|
65379
|
+
while (nl !== -1) {
|
|
65380
|
+
yield carry.slice(0, nl);
|
|
65381
|
+
carry = carry.slice(nl + 1);
|
|
65382
|
+
nl = carry.indexOf(`
|
|
65383
|
+
`);
|
|
65384
|
+
}
|
|
65385
|
+
}
|
|
65386
|
+
carry += decoder.decode();
|
|
65387
|
+
if (carry.length > 0)
|
|
65388
|
+
yield carry;
|
|
65389
|
+
}
|
|
65390
|
+
|
|
64960
65391
|
// src/plugins/builtin/curator/rollup.ts
|
|
64961
65392
|
import { appendFile as appendFile3, mkdir as mkdir9, writeFile } from "fs/promises";
|
|
64962
65393
|
import * as path19 from "path";
|
|
@@ -64977,7 +65408,72 @@ async function appendToRollup(observations, rollupPath) {
|
|
|
64977
65408
|
await appendFile3(rollupPath, newLines);
|
|
64978
65409
|
} catch {}
|
|
64979
65410
|
}
|
|
64980
|
-
|
|
65411
|
+
function emptyWindow() {
|
|
65412
|
+
return { observations: [], runIds: [], truncated: false, unattributedRows: 0 };
|
|
65413
|
+
}
|
|
65414
|
+
async function parseTail(file3, startedMidFile, projectKey) {
|
|
65415
|
+
const observations = [];
|
|
65416
|
+
let unattributed = 0;
|
|
65417
|
+
let first = true;
|
|
65418
|
+
for await (const line of streamJsonlLines(file3)) {
|
|
65419
|
+
if (first) {
|
|
65420
|
+
first = false;
|
|
65421
|
+
if (startedMidFile)
|
|
65422
|
+
continue;
|
|
65423
|
+
}
|
|
65424
|
+
if (!line.trim())
|
|
65425
|
+
continue;
|
|
65426
|
+
try {
|
|
65427
|
+
const obs = JSON.parse(line);
|
|
65428
|
+
if (obs.projectKey === projectKey)
|
|
65429
|
+
observations.push(obs);
|
|
65430
|
+
else if (!obs.projectKey)
|
|
65431
|
+
unattributed += 1;
|
|
65432
|
+
} catch {}
|
|
65433
|
+
}
|
|
65434
|
+
return { observations, unattributed };
|
|
65435
|
+
}
|
|
65436
|
+
function newestRunIds(observations, windowRuns) {
|
|
65437
|
+
const keep = new Set;
|
|
65438
|
+
for (let i = observations.length - 1;i >= 0 && keep.size < windowRuns; i -= 1) {
|
|
65439
|
+
const runId = observations[i]?.runId;
|
|
65440
|
+
if (runId)
|
|
65441
|
+
keep.add(runId);
|
|
65442
|
+
}
|
|
65443
|
+
return keep;
|
|
65444
|
+
}
|
|
65445
|
+
async function readHeuristicWindow(rollupPath, windowRuns, options) {
|
|
65446
|
+
const maxTail = Math.max(1, options.maxTailBytes ?? MAX_WINDOW_TAIL_BYTES);
|
|
65447
|
+
try {
|
|
65448
|
+
const file3 = Bun.file(rollupPath);
|
|
65449
|
+
if (!await file3.exists())
|
|
65450
|
+
return emptyWindow();
|
|
65451
|
+
const size = file3.size;
|
|
65452
|
+
let tail = Math.max(1, Math.min(options.tailBytes ?? INITIAL_WINDOW_TAIL_BYTES, maxTail));
|
|
65453
|
+
while (true) {
|
|
65454
|
+
const start = Math.max(0, size - tail);
|
|
65455
|
+
const { observations, unattributed } = await parseTail(start > 0 ? file3.slice(start) : file3, start > 0, options.projectKey);
|
|
65456
|
+
const keep = newestRunIds(observations, windowRuns);
|
|
65457
|
+
const exhausted = start === 0;
|
|
65458
|
+
if (keep.size >= windowRuns || exhausted || tail >= maxTail) {
|
|
65459
|
+
return {
|
|
65460
|
+
observations: observations.filter((o) => keep.has(o.runId)),
|
|
65461
|
+
runIds: [...keep],
|
|
65462
|
+
truncated: keep.size < windowRuns && !exhausted,
|
|
65463
|
+
unattributedRows: unattributed
|
|
65464
|
+
};
|
|
65465
|
+
}
|
|
65466
|
+
tail = Math.max(1, Math.min(tail * 2, maxTail));
|
|
65467
|
+
}
|
|
65468
|
+
} catch {
|
|
65469
|
+
return emptyWindow();
|
|
65470
|
+
}
|
|
65471
|
+
}
|
|
65472
|
+
var INITIAL_WINDOW_TAIL_BYTES, MAX_WINDOW_TAIL_BYTES;
|
|
65473
|
+
var init_rollup = __esm(() => {
|
|
65474
|
+
INITIAL_WINDOW_TAIL_BYTES = 8 * 1024 * 1024;
|
|
65475
|
+
MAX_WINDOW_TAIL_BYTES = 64 * 1024 * 1024;
|
|
65476
|
+
});
|
|
64981
65477
|
|
|
64982
65478
|
// src/plugins/builtin/curator/index.ts
|
|
64983
65479
|
import { mkdir as mkdir10 } from "fs/promises";
|
|
@@ -65022,11 +65518,12 @@ function getCuratorThresholds(context) {
|
|
|
65022
65518
|
unchangedOutcome: raw.unchangedOutcome ?? DEFAULT_THRESHOLDS3.unchangedOutcome
|
|
65023
65519
|
};
|
|
65024
65520
|
}
|
|
65025
|
-
var PLUGIN_NAME3 = "nax-curator", PLUGIN_VERSION3 = "0.1.0", DEFAULT_THRESHOLDS3, curatorAction, curatorPlugin;
|
|
65521
|
+
var PLUGIN_NAME3 = "nax-curator", PLUGIN_VERSION3 = "0.1.0", DEFAULT_THRESHOLDS3, curatorAction, HEURISTIC_WINDOW_RUNS = 20, curatorPlugin;
|
|
65026
65522
|
var init_curator = __esm(() => {
|
|
65027
65523
|
init_collect();
|
|
65028
65524
|
init_heuristics();
|
|
65029
65525
|
init_rollup();
|
|
65526
|
+
init_rollup();
|
|
65030
65527
|
DEFAULT_THRESHOLDS3 = {
|
|
65031
65528
|
repeatedFinding: 2,
|
|
65032
65529
|
emptyKeyword: 2,
|
|
@@ -65059,12 +65556,22 @@ var init_curator = __esm(() => {
|
|
|
65059
65556
|
await Bun.write(observationsPath, observations.map((o) => JSON.stringify(o)).join(`
|
|
65060
65557
|
`) + (observations.length > 0 ? `
|
|
65061
65558
|
` : ""));
|
|
65559
|
+
await appendToRollup(observations, rollupPath);
|
|
65062
65560
|
const thresholds = getCuratorThresholds(context);
|
|
65063
|
-
const
|
|
65561
|
+
const window2 = await readHeuristicWindow(rollupPath, HEURISTIC_WINDOW_RUNS, {
|
|
65562
|
+
projectKey: curatorContext.projectKey
|
|
65563
|
+
});
|
|
65564
|
+
if (window2.truncated) {
|
|
65565
|
+
context.logger.warn("Curator window truncated at the byte ceiling", {
|
|
65566
|
+
runsFound: window2.runIds.length,
|
|
65567
|
+
runsRequested: HEURISTIC_WINDOW_RUNS,
|
|
65568
|
+
unattributedRows: window2.unattributedRows
|
|
65569
|
+
});
|
|
65570
|
+
}
|
|
65571
|
+
const proposals = runHeuristics(window2.observations.length > 0 ? window2.observations : observations, thresholds);
|
|
65064
65572
|
const markdown = renderProposals(proposals, context.runId, observations.length);
|
|
65065
65573
|
const proposalsMdPath = path20.join(runDir, "curator-proposals.md");
|
|
65066
65574
|
await Bun.write(proposalsMdPath, markdown);
|
|
65067
|
-
await appendToRollup(observations, rollupPath);
|
|
65068
65575
|
}
|
|
65069
65576
|
return {
|
|
65070
65577
|
success: true,
|
|
@@ -65097,15 +65604,21 @@ function selectFinish(config2) {
|
|
|
65097
65604
|
return;
|
|
65098
65605
|
return finishConfigSelector.select(config2)?.finish;
|
|
65099
65606
|
}
|
|
65607
|
+
function resolveFlowAgent(config2, explicit) {
|
|
65608
|
+
if (typeof explicit === "string" && explicit.length > 0)
|
|
65609
|
+
return explicit;
|
|
65610
|
+
return resolveDefaultAgent(config2 ?? {});
|
|
65611
|
+
}
|
|
65100
65612
|
function getFinishAutoFlowConfig(ctx) {
|
|
65101
65613
|
const autoFlow = selectFinish(ctx.config)?.autoFlow;
|
|
65102
65614
|
if (!autoFlow)
|
|
65103
|
-
return DEFAULT_FINISH_AUTO_FLOW_CONFIG;
|
|
65615
|
+
return { ...DEFAULT_FINISH_AUTO_FLOW_CONFIG, defaultAgent: resolveFlowAgent(ctx.config, null) };
|
|
65104
65616
|
const defaults = DEFAULT_FINISH_AUTO_FLOW_CONFIG;
|
|
65105
65617
|
return {
|
|
65106
65618
|
enabled: autoFlow.enabled === true,
|
|
65107
65619
|
flowPath: autoFlow.flowPath ?? defaults.flowPath,
|
|
65108
|
-
defaultAgent: autoFlow.defaultAgent
|
|
65620
|
+
defaultAgent: resolveFlowAgent(ctx.config, autoFlow.defaultAgent),
|
|
65621
|
+
model: autoFlow.model ?? null,
|
|
65109
65622
|
reviewers: {
|
|
65110
65623
|
spec: autoFlow.reviewers?.spec ?? null,
|
|
65111
65624
|
quality: autoFlow.reviewers?.quality ?? null
|
|
@@ -65129,11 +65642,12 @@ function telegramCreds(config2) {
|
|
|
65129
65642
|
}
|
|
65130
65643
|
var DEFAULT_FINISH_AUTO_FLOW_CONFIG;
|
|
65131
65644
|
var init_config2 = __esm(() => {
|
|
65645
|
+
init_agents();
|
|
65132
65646
|
init_config();
|
|
65133
65647
|
DEFAULT_FINISH_AUTO_FLOW_CONFIG = {
|
|
65134
65648
|
enabled: false,
|
|
65135
65649
|
flowPath: "flows/nax-finish/nax-finish.flow.ts",
|
|
65136
|
-
|
|
65650
|
+
model: null,
|
|
65137
65651
|
reviewers: { spec: null, quality: null },
|
|
65138
65652
|
escalate: { telegram: true },
|
|
65139
65653
|
notify: { mode: "escalation" },
|
|
@@ -65228,14 +65742,21 @@ async function defaultRun2(cmd, opts) {
|
|
|
65228
65742
|
clearTimeout(timer);
|
|
65229
65743
|
}
|
|
65230
65744
|
}
|
|
65231
|
-
|
|
65232
|
-
const
|
|
65745
|
+
function finishAuditDir(ctx) {
|
|
65746
|
+
const root = ctx.outputDir ?? path21.join(ctx.workdir, ".nax");
|
|
65747
|
+
return path21.join(root, "finish-audit", ctx.feature);
|
|
65748
|
+
}
|
|
65749
|
+
function finishResultPath(ctx, runId) {
|
|
65750
|
+
return path21.join(finishAuditDir(ctx), `${runId}.result.json`);
|
|
65751
|
+
}
|
|
65752
|
+
async function defaultReadResult(resultPath) {
|
|
65753
|
+
const f = Bun.file(resultPath);
|
|
65233
65754
|
if (!await f.exists())
|
|
65234
65755
|
return null;
|
|
65235
65756
|
return JSON.parse(await f.text());
|
|
65236
65757
|
}
|
|
65237
|
-
async function defaultClearResult(
|
|
65238
|
-
const file3 = Bun.file(
|
|
65758
|
+
async function defaultClearResult(resultPath) {
|
|
65759
|
+
const file3 = Bun.file(resultPath);
|
|
65239
65760
|
if (await file3.exists())
|
|
65240
65761
|
await file3.delete();
|
|
65241
65762
|
}
|
|
@@ -65260,18 +65781,19 @@ async function resolveFlowPath(workdir, flowPath, deps = _naxFinishDeps) {
|
|
|
65260
65781
|
}
|
|
65261
65782
|
return null;
|
|
65262
65783
|
}
|
|
65263
|
-
function buildFlowArgv(flowPath, inputJson,
|
|
65264
|
-
const stepTimeout = stepMs && stepMs > 0 ? ["--timeout", String(Math.ceil(stepMs / 1000))] : [];
|
|
65784
|
+
function buildFlowArgv(flowPath, inputJson, opts = {}) {
|
|
65785
|
+
const stepTimeout = opts.stepMs && opts.stepMs > 0 ? ["--timeout", String(Math.ceil(opts.stepMs / 1000))] : [];
|
|
65265
65786
|
return [
|
|
65266
65787
|
"acpx",
|
|
65267
65788
|
"--approve-all",
|
|
65268
65789
|
...stepTimeout,
|
|
65790
|
+
...opts.model ? ["--model", opts.model] : [],
|
|
65269
65791
|
"flow",
|
|
65270
65792
|
"run",
|
|
65271
65793
|
flowPath,
|
|
65272
65794
|
"--input-json",
|
|
65273
65795
|
inputJson,
|
|
65274
|
-
...defaultAgent ? ["--default-agent", defaultAgent] : []
|
|
65796
|
+
...opts.defaultAgent ? ["--default-agent", opts.defaultAgent] : []
|
|
65275
65797
|
];
|
|
65276
65798
|
}
|
|
65277
65799
|
function buildFlowEnv(cfg) {
|
|
@@ -65309,22 +65831,29 @@ async function executeFinishFlow(options) {
|
|
|
65309
65831
|
escalateTelegram
|
|
65310
65832
|
};
|
|
65311
65833
|
}
|
|
65312
|
-
|
|
65834
|
+
const resultPath = finishResultPath(ctx, ctx.runId);
|
|
65835
|
+
await _naxFinishDeps.clearResult(resultPath);
|
|
65313
65836
|
const input = {
|
|
65314
65837
|
feature: ctx.feature,
|
|
65315
65838
|
workdir: ctx.workdir,
|
|
65316
65839
|
branch: ctx.branch,
|
|
65317
65840
|
prdPath: ctx.prdPath,
|
|
65841
|
+
auditDir: finishAuditDir(ctx),
|
|
65842
|
+
runId: ctx.runId,
|
|
65318
65843
|
escalateTelegram,
|
|
65319
65844
|
timeouts: { acceptanceMs: cfg.timeouts.acceptanceMs, gateMs: cfg.timeouts.gateMs }
|
|
65320
65845
|
};
|
|
65321
|
-
const cmd = buildFlowArgv(flowPath, JSON.stringify(input),
|
|
65846
|
+
const cmd = buildFlowArgv(flowPath, JSON.stringify(input), {
|
|
65847
|
+
defaultAgent: cfg.defaultAgent,
|
|
65848
|
+
stepMs: cfg.timeouts.stepMs,
|
|
65849
|
+
model: cfg.model
|
|
65850
|
+
});
|
|
65322
65851
|
const res = await _naxFinishDeps.run(cmd, {
|
|
65323
65852
|
cwd: ctx.workdir,
|
|
65324
65853
|
env: buildFlowEnv(cfg),
|
|
65325
65854
|
timeoutMs: cfg.timeouts.flowMs
|
|
65326
65855
|
});
|
|
65327
|
-
const result = await _naxFinishDeps.readResult(
|
|
65856
|
+
const result = await _naxFinishDeps.readResult(resultPath);
|
|
65328
65857
|
if (!result)
|
|
65329
65858
|
return missingResultOutcome(ctx, res, escalateTelegram);
|
|
65330
65859
|
return {
|
|
@@ -65702,7 +66231,7 @@ function startHeartbeat(opts) {
|
|
|
65702
66231
|
let stopped = false;
|
|
65703
66232
|
let timer;
|
|
65704
66233
|
const armTimer = () => {
|
|
65705
|
-
timer = setTimeout(() => {
|
|
66234
|
+
timer = _heartbeatDeps.setTimeout(() => {
|
|
65706
66235
|
try {
|
|
65707
66236
|
Promise.resolve(onTick(getSnapshot())).catch((err) => getSafeLogger()?.warn(STAGE2, "Heartbeat tick failed", {
|
|
65708
66237
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -65721,7 +66250,7 @@ function startHeartbeat(opts) {
|
|
|
65721
66250
|
stop() {
|
|
65722
66251
|
stopped = true;
|
|
65723
66252
|
if (timer !== undefined)
|
|
65724
|
-
clearTimeout(timer);
|
|
66253
|
+
_heartbeatDeps.clearTimeout(timer);
|
|
65725
66254
|
}
|
|
65726
66255
|
};
|
|
65727
66256
|
}
|
|
@@ -65765,10 +66294,14 @@ function buildHeartbeatMetricsPayload(p) {
|
|
|
65765
66294
|
]
|
|
65766
66295
|
};
|
|
65767
66296
|
}
|
|
65768
|
-
var STAGE2 = "otel-reporter-heartbeat";
|
|
66297
|
+
var STAGE2 = "otel-reporter-heartbeat", _heartbeatDeps;
|
|
65769
66298
|
var init_heartbeat = __esm(() => {
|
|
65770
66299
|
init_logger2();
|
|
65771
66300
|
init_otlp();
|
|
66301
|
+
_heartbeatDeps = {
|
|
66302
|
+
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
66303
|
+
clearTimeout: (id) => clearTimeout(id)
|
|
66304
|
+
};
|
|
65772
66305
|
});
|
|
65773
66306
|
|
|
65774
66307
|
// src/plugins/builtin/otel-reporter/ids.ts
|
|
@@ -65798,7 +66331,7 @@ function toLogRecord(entry) {
|
|
|
65798
66331
|
const nonScalars = {};
|
|
65799
66332
|
for (const [key, value] of Object.entries(data)) {
|
|
65800
66333
|
if (typeof value === "string") {
|
|
65801
|
-
attributes.push(attr(`nax.data.${key}`,
|
|
66334
|
+
attributes.push(attr(`nax.data.${key}`, truncate4(value)));
|
|
65802
66335
|
} else if (typeof value === "number") {
|
|
65803
66336
|
if (Number.isFinite(value)) {
|
|
65804
66337
|
attributes.push(attr(`nax.data.${key}`, value));
|
|
@@ -65812,7 +66345,7 @@ function toLogRecord(entry) {
|
|
|
65812
66345
|
}
|
|
65813
66346
|
}
|
|
65814
66347
|
if (Object.keys(nonScalars).length > 0) {
|
|
65815
|
-
attributes.push(attr("nax.data_json",
|
|
66348
|
+
attributes.push(attr("nax.data_json", truncate4(JSON.stringify(nonScalars))));
|
|
65816
66349
|
}
|
|
65817
66350
|
return {
|
|
65818
66351
|
body: { stringValue: entry.message },
|
|
@@ -65835,7 +66368,7 @@ function buildLogsPayload(entries, resource) {
|
|
|
65835
66368
|
]
|
|
65836
66369
|
};
|
|
65837
66370
|
}
|
|
65838
|
-
function
|
|
66371
|
+
function truncate4(value) {
|
|
65839
66372
|
if (value.length <= DATA_JSON_MAX)
|
|
65840
66373
|
return value;
|
|
65841
66374
|
const marker = TRUNCATION_MARKER;
|
|
@@ -66803,11 +67336,11 @@ function getSafeLogger6() {
|
|
|
66803
67336
|
return getSafeLogger();
|
|
66804
67337
|
}
|
|
66805
67338
|
function extractPluginName(pluginPath) {
|
|
66806
|
-
const
|
|
66807
|
-
if (
|
|
67339
|
+
const basename13 = path22.basename(pluginPath);
|
|
67340
|
+
if (basename13 === "index.ts" || basename13 === "index.js" || basename13 === "index.mjs") {
|
|
66808
67341
|
return path22.basename(path22.dirname(pluginPath));
|
|
66809
67342
|
}
|
|
66810
|
-
return
|
|
67343
|
+
return basename13.replace(/\.(ts|js|mjs)$/, "");
|
|
66811
67344
|
}
|
|
66812
67345
|
async function loadPlugins(globalDir, projectDir, configPlugins, projectRoot, disabledPlugins, isTestFileFn, reporters) {
|
|
66813
67346
|
const loadedPlugins = [];
|
|
@@ -67480,9 +68013,9 @@ var init_hooks = __esm(() => {
|
|
|
67480
68013
|
// src/execution/crash-heartbeat.ts
|
|
67481
68014
|
import { appendFileSync as appendFileSync2 } from "fs";
|
|
67482
68015
|
async function heartbeatLoop(gen, statusWriter, getTotalCost, getIterations, jsonlFilePath) {
|
|
67483
|
-
const logger =
|
|
68016
|
+
const logger = _heartbeatDeps2.getSafeLogger();
|
|
67484
68017
|
while (gen === _heartbeatGen && _heartbeatActive) {
|
|
67485
|
-
await
|
|
68018
|
+
await _heartbeatDeps2.sleep(60000);
|
|
67486
68019
|
if (gen !== _heartbeatGen || !_heartbeatActive)
|
|
67487
68020
|
break;
|
|
67488
68021
|
try {
|
|
@@ -67511,11 +68044,11 @@ async function heartbeatLoop(gen, statusWriter, getTotalCost, getIterations, jso
|
|
|
67511
68044
|
}
|
|
67512
68045
|
}
|
|
67513
68046
|
function startHeartbeat2(statusWriter, getTotalCost, getIterations, jsonlFilePath) {
|
|
67514
|
-
const logger =
|
|
68047
|
+
const logger = _heartbeatDeps2.getSafeLogger();
|
|
67515
68048
|
_heartbeatActive = true;
|
|
67516
68049
|
const gen = ++_heartbeatGen;
|
|
67517
68050
|
heartbeatLoop(gen, statusWriter, getTotalCost, getIterations, jsonlFilePath).catch((err) => {
|
|
67518
|
-
|
|
68051
|
+
_heartbeatDeps2.getSafeLogger()?.warn("crash-recovery", "Heartbeat loop crashed; status updates stopped", {
|
|
67519
68052
|
error: err instanceof Error ? err.message : String(err)
|
|
67520
68053
|
});
|
|
67521
68054
|
});
|
|
@@ -67528,10 +68061,10 @@ function stopHeartbeat() {
|
|
|
67528
68061
|
getSafeLogger()?.debug("crash-recovery", "Heartbeat stopped");
|
|
67529
68062
|
}
|
|
67530
68063
|
}
|
|
67531
|
-
var
|
|
68064
|
+
var _heartbeatDeps2, _heartbeatGen = 0, _heartbeatActive = false;
|
|
67532
68065
|
var init_crash_heartbeat = __esm(() => {
|
|
67533
68066
|
init_logger2();
|
|
67534
|
-
|
|
68067
|
+
_heartbeatDeps2 = {
|
|
67535
68068
|
sleep: async (ms) => Bun.sleep(ms),
|
|
67536
68069
|
getSafeLogger
|
|
67537
68070
|
};
|
|
@@ -68306,6 +68839,7 @@ function buildAcceptanceContext(ctx, prd) {
|
|
|
68306
68839
|
agentManager: ctx.agentManager,
|
|
68307
68840
|
sessionManager: ctx.sessionManager,
|
|
68308
68841
|
acceptanceTestPaths: ctx.acceptanceTestPaths,
|
|
68842
|
+
acceptanceRetries: ctx.acceptanceRetries ?? 0,
|
|
68309
68843
|
runtime: ctx.runtime,
|
|
68310
68844
|
abortSignal: ctx.abortSignal
|
|
68311
68845
|
};
|
|
@@ -68402,8 +68936,9 @@ async function runAcceptanceLoop(ctx) {
|
|
|
68402
68936
|
logger?.info("acceptance", "All stories complete, running acceptance validation");
|
|
68403
68937
|
const { acceptanceStage: acceptanceStage2 } = await _runAcceptanceTestsOnceDeps.importAcceptanceStage();
|
|
68404
68938
|
while (acceptanceRetries < maxRetries) {
|
|
68939
|
+
const attemptCtx = { ...ctx, acceptanceRetries };
|
|
68405
68940
|
const firstStory = prd.userStories[0];
|
|
68406
|
-
const acceptanceContext = buildAcceptanceContext(
|
|
68941
|
+
const acceptanceContext = buildAcceptanceContext(attemptCtx, prd);
|
|
68407
68942
|
const acceptanceResult = await acceptanceStage2.execute(acceptanceContext);
|
|
68408
68943
|
if (acceptanceResult.action === "continue") {
|
|
68409
68944
|
logger?.info("acceptance", "Acceptance validation passed!");
|
|
@@ -68494,17 +69029,14 @@ async function runAcceptanceLoop(ctx) {
|
|
|
68494
69029
|
confidence: diagnosis.confidence,
|
|
68495
69030
|
attempt: acceptanceRetries
|
|
68496
69031
|
});
|
|
68497
|
-
const cycleResult = await runAcceptanceFixCycle(
|
|
68498
|
-
packageDir: pkg.packageDir,
|
|
68499
|
-
testPath: effectivePath
|
|
68500
|
-
});
|
|
69032
|
+
const cycleResult = await runAcceptanceFixCycle(attemptCtx, prd, pkgFailures, diagnosis, effectivePath, testCommand, { packageDir: pkg.packageDir, testPath: effectivePath });
|
|
68501
69033
|
totalCost2 += cycleResult.costUsd ?? 0;
|
|
68502
69034
|
totalInternalIterations += cycleResult.iterations.length;
|
|
68503
69035
|
const pkgResolved = cycleResult.exitReason === "resolved" || cycleResult.finalFindings.length === 0;
|
|
68504
69036
|
if (!pkgResolved)
|
|
68505
69037
|
remainingFindings.push(...cycleResult.finalFindings);
|
|
68506
69038
|
}
|
|
68507
|
-
const finalCheck = await runAcceptanceTestsOnce(
|
|
69039
|
+
const finalCheck = await runAcceptanceTestsOnce(attemptCtx, prd);
|
|
68508
69040
|
const success2 = finalCheck.passed && remainingFindings.length === 0;
|
|
68509
69041
|
const failureMessages = !success2 ? finalCheck.failedACs.length > 0 ? finalCheck.failedACs : remainingFindings.length > 0 ? remainingFindings.map((f) => f.message) : ["acceptance validation failed (unknown cause)"] : undefined;
|
|
68510
69042
|
return buildResult(success2, prd, totalCost2, iterations, storiesCompleted, prdDirty, failureMessages, acceptanceRetries + totalInternalIterations);
|
|
@@ -69812,10 +70344,10 @@ var init_ensure_package_dirs = __esm(() => {
|
|
|
69812
70344
|
|
|
69813
70345
|
// src/pipeline/subscribers/events-writer.ts
|
|
69814
70346
|
import { appendFile as appendFile5, mkdir as mkdir13 } from "fs/promises";
|
|
69815
|
-
import { basename as
|
|
70347
|
+
import { basename as basename16, join as join86 } from "path";
|
|
69816
70348
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
69817
70349
|
const logger = getSafeLogger();
|
|
69818
|
-
const project =
|
|
70350
|
+
const project = basename16(workdir);
|
|
69819
70351
|
const eventsDir = join86(getEventsRootDir(), project);
|
|
69820
70352
|
const eventsFile = join86(eventsDir, "events.jsonl");
|
|
69821
70353
|
let dirReady = false;
|
|
@@ -69998,10 +70530,10 @@ var init_interaction2 = __esm(() => {
|
|
|
69998
70530
|
|
|
69999
70531
|
// src/pipeline/subscribers/registry.ts
|
|
70000
70532
|
import { mkdir as mkdir14, writeFile as writeFile2 } from "fs/promises";
|
|
70001
|
-
import { basename as
|
|
70533
|
+
import { basename as basename17, join as join87 } from "path";
|
|
70002
70534
|
function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
70003
70535
|
const logger = getSafeLogger();
|
|
70004
|
-
const project =
|
|
70536
|
+
const project = basename17(workdir);
|
|
70005
70537
|
const runDir = join87(getRunsDir(), `${project}-${feature}-${runId}`);
|
|
70006
70538
|
const metaFile = join87(runDir, "meta.json");
|
|
70007
70539
|
const unsub = bus.on("run:started", (_ev) => {
|
|
@@ -73921,7 +74453,8 @@ function buildPostRunContext(opts, durationMs, logger) {
|
|
|
73921
74453
|
projectKey,
|
|
73922
74454
|
curatorRollupPath: curatorRollupPath2,
|
|
73923
74455
|
logFilePath,
|
|
73924
|
-
config: config2
|
|
74456
|
+
config: config2,
|
|
74457
|
+
startTime
|
|
73925
74458
|
} = opts;
|
|
73926
74459
|
const counts = countStories(prd);
|
|
73927
74460
|
return {
|
|
@@ -73932,6 +74465,7 @@ function buildPostRunContext(opts, durationMs, logger) {
|
|
|
73932
74465
|
branch,
|
|
73933
74466
|
version: version2,
|
|
73934
74467
|
totalDurationMs: durationMs,
|
|
74468
|
+
runStartedAt: startTime,
|
|
73935
74469
|
totalCost: totalCost2,
|
|
73936
74470
|
storySummary: {
|
|
73937
74471
|
completed: storiesCompleted,
|
|
@@ -103897,6 +104431,88 @@ var init_bakeoff = __esm(() => {
|
|
|
103897
104431
|
init_report2();
|
|
103898
104432
|
});
|
|
103899
104433
|
|
|
104434
|
+
// src/plugins/builtin/curator/rollup-prune.ts
|
|
104435
|
+
import { rename as rename4, unlink as unlink4, writeFile as writeFile3 } from "fs/promises";
|
|
104436
|
+
import { appendFile as appendFile6 } from "fs/promises";
|
|
104437
|
+
async function scanProjectRunIds(rollupPath, projectKey) {
|
|
104438
|
+
const maxTsByRunId = new Map;
|
|
104439
|
+
for await (const line of streamJsonlLines(Bun.file(rollupPath))) {
|
|
104440
|
+
if (!line.trim())
|
|
104441
|
+
continue;
|
|
104442
|
+
let obs;
|
|
104443
|
+
try {
|
|
104444
|
+
obs = JSON.parse(line);
|
|
104445
|
+
} catch {
|
|
104446
|
+
continue;
|
|
104447
|
+
}
|
|
104448
|
+
if (obs.projectKey !== projectKey)
|
|
104449
|
+
continue;
|
|
104450
|
+
const existing = maxTsByRunId.get(obs.runId);
|
|
104451
|
+
if (existing === undefined || obs.ts > existing)
|
|
104452
|
+
maxTsByRunId.set(obs.runId, obs.ts);
|
|
104453
|
+
}
|
|
104454
|
+
return [...maxTsByRunId.entries()].sort((a, b) => a[1] > b[1] ? -1 : a[1] < b[1] ? 1 : 0).map(([runId]) => runId);
|
|
104455
|
+
}
|
|
104456
|
+
async function pruneRollup(input) {
|
|
104457
|
+
const { rollupPath, projectKey, keepRunIds, dropUnattributed = false } = input;
|
|
104458
|
+
const tmpPath = `${rollupPath}.gc-tmp`;
|
|
104459
|
+
const result2 = { kept: 0, dropped: 0, keptOtherProjects: 0, keptUnattributed: 0 };
|
|
104460
|
+
let buffer = "";
|
|
104461
|
+
const flush = async () => {
|
|
104462
|
+
if (buffer.length === 0)
|
|
104463
|
+
return;
|
|
104464
|
+
await appendFile6(tmpPath, buffer);
|
|
104465
|
+
buffer = "";
|
|
104466
|
+
};
|
|
104467
|
+
try {
|
|
104468
|
+
await writeFile3(tmpPath, "");
|
|
104469
|
+
for await (const line of streamJsonlLines(Bun.file(rollupPath))) {
|
|
104470
|
+
if (!line.trim())
|
|
104471
|
+
continue;
|
|
104472
|
+
let obs = null;
|
|
104473
|
+
try {
|
|
104474
|
+
obs = JSON.parse(line);
|
|
104475
|
+
} catch {
|
|
104476
|
+
obs = null;
|
|
104477
|
+
}
|
|
104478
|
+
let keep;
|
|
104479
|
+
if (obs === null) {
|
|
104480
|
+
keep = !dropUnattributed;
|
|
104481
|
+
if (keep)
|
|
104482
|
+
result2.keptUnattributed++;
|
|
104483
|
+
} else if (obs.projectKey === undefined) {
|
|
104484
|
+
keep = !dropUnattributed;
|
|
104485
|
+
if (keep)
|
|
104486
|
+
result2.keptUnattributed++;
|
|
104487
|
+
} else if (obs.projectKey !== projectKey) {
|
|
104488
|
+
keep = true;
|
|
104489
|
+
result2.keptOtherProjects++;
|
|
104490
|
+
} else {
|
|
104491
|
+
keep = keepRunIds.has(obs.runId);
|
|
104492
|
+
}
|
|
104493
|
+
if (!keep) {
|
|
104494
|
+
result2.dropped++;
|
|
104495
|
+
continue;
|
|
104496
|
+
}
|
|
104497
|
+
result2.kept++;
|
|
104498
|
+
buffer += `${line}
|
|
104499
|
+
`;
|
|
104500
|
+
if (buffer.length >= FLUSH_BYTES)
|
|
104501
|
+
await flush();
|
|
104502
|
+
}
|
|
104503
|
+
await flush();
|
|
104504
|
+
await rename4(tmpPath, rollupPath);
|
|
104505
|
+
} catch (err) {
|
|
104506
|
+
await unlink4(tmpPath).catch(() => {});
|
|
104507
|
+
throw err;
|
|
104508
|
+
}
|
|
104509
|
+
return result2;
|
|
104510
|
+
}
|
|
104511
|
+
var FLUSH_BYTES;
|
|
104512
|
+
var init_rollup_prune = __esm(() => {
|
|
104513
|
+
FLUSH_BYTES = 4 * 1024 * 1024;
|
|
104514
|
+
});
|
|
104515
|
+
|
|
103900
104516
|
// src/commands/curator.ts
|
|
103901
104517
|
var exports_curator = {};
|
|
103902
104518
|
__export(exports_curator, {
|
|
@@ -103904,14 +104520,12 @@ __export(exports_curator, {
|
|
|
103904
104520
|
curatorGc: () => curatorGc,
|
|
103905
104521
|
curatorDryrun: () => curatorDryrun,
|
|
103906
104522
|
curatorCommit: () => curatorCommit,
|
|
104523
|
+
_testing: () => _testing,
|
|
103907
104524
|
_curatorCmdDeps: () => _curatorCmdDeps
|
|
103908
104525
|
});
|
|
103909
104526
|
import { readdirSync as readdirSync9 } from "fs";
|
|
103910
|
-
import { unlink as
|
|
103911
|
-
import {
|
|
103912
|
-
function getProjectKey(config2, projectDir) {
|
|
103913
|
-
return config2.name?.trim() || basename18(projectDir);
|
|
103914
|
-
}
|
|
104527
|
+
import { unlink as unlink5 } from "fs/promises";
|
|
104528
|
+
import { join as join100 } from "path";
|
|
103915
104529
|
function listRunIds(runsDir) {
|
|
103916
104530
|
try {
|
|
103917
104531
|
return readdirSync9(runsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
@@ -104153,34 +104767,25 @@ async function curatorGc(options) {
|
|
|
104153
104767
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
104154
104768
|
const gDir = _curatorCmdDeps.globalOutputDir();
|
|
104155
104769
|
const rollupPath = _curatorCmdDeps.curatorRollupPath(gDir, config2.curator?.rollupPath);
|
|
104156
|
-
|
|
104157
|
-
if (rollupText === null) {
|
|
104770
|
+
if (!await _curatorCmdDeps.fileExists(rollupPath)) {
|
|
104158
104771
|
console.log(`[gc] No rollup file found at ${rollupPath}. Nothing to prune.`);
|
|
104159
104772
|
return;
|
|
104160
104773
|
}
|
|
104161
|
-
const
|
|
104162
|
-
|
|
104163
|
-
const observations = lines.map((l) => JSON.parse(l));
|
|
104164
|
-
const maxTsByRunId = new Map;
|
|
104165
|
-
for (const obs of observations) {
|
|
104166
|
-
const existing = maxTsByRunId.get(obs.runId);
|
|
104167
|
-
if (!existing || obs.ts > existing) {
|
|
104168
|
-
maxTsByRunId.set(obs.runId, obs.ts);
|
|
104169
|
-
}
|
|
104170
|
-
}
|
|
104774
|
+
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
104775
|
+
const uniqueRunIds = await _curatorCmdDeps.scanProjectRunIds(rollupPath, projectKey);
|
|
104171
104776
|
const keep = options.keep ?? DEFAULT_KEEP;
|
|
104172
|
-
const
|
|
104173
|
-
if (uniqueRunIds.length <= keep) {
|
|
104174
|
-
console.log(`[gc] ${uniqueRunIds.length} unique run(s) in rollup \u2014 at or below keep=${keep}. Nothing to prune.`);
|
|
104777
|
+
const sweep = options.sweepUnattributed === true;
|
|
104778
|
+
if (uniqueRunIds.length <= keep && !sweep) {
|
|
104779
|
+
console.log(`[gc] ${uniqueRunIds.length} unique run(s) for ${projectKey} in rollup \u2014 at or below keep=${keep}. Nothing to prune.`);
|
|
104175
104780
|
return;
|
|
104176
104781
|
}
|
|
104177
104782
|
const keepSet = new Set(uniqueRunIds.slice(0, keep));
|
|
104178
|
-
const
|
|
104179
|
-
|
|
104180
|
-
|
|
104181
|
-
|
|
104182
|
-
|
|
104183
|
-
|
|
104783
|
+
const result2 = await _curatorCmdDeps.pruneRollup({
|
|
104784
|
+
rollupPath,
|
|
104785
|
+
projectKey,
|
|
104786
|
+
keepRunIds: keepSet,
|
|
104787
|
+
dropUnattributed: sweep
|
|
104788
|
+
});
|
|
104184
104789
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
104185
104790
|
const perRunsDir = join100(outputDir, "runs");
|
|
104186
104791
|
for (const runId of uniqueRunIds) {
|
|
@@ -104190,12 +104795,20 @@ async function curatorGc(options) {
|
|
|
104190
104795
|
await _curatorCmdDeps.removeFile(join100(runDir, "curator-proposals.md"));
|
|
104191
104796
|
}
|
|
104192
104797
|
}
|
|
104193
|
-
|
|
104798
|
+
const droppedRuns = Math.max(0, uniqueRunIds.length - keepSet.size);
|
|
104799
|
+
console.log(`[gc] Pruned rollup for ${projectKey}: kept ${keepSet.size} of ${uniqueRunIds.length} run(s), dropped ${result2.dropped} row(s).`);
|
|
104800
|
+
console.log(`[gc] Preserved ${result2.keptOtherProjects} row(s) from other projects and ${result2.keptUnattributed} unattributed row(s).`);
|
|
104801
|
+
if (!sweep && result2.keptUnattributed > 0) {
|
|
104802
|
+
console.log(`[gc] ${result2.keptUnattributed} unattributed row(s) predate project scoping (#1429) and can never be read back. Run with --sweep-unattributed to drop them machine-wide.`);
|
|
104803
|
+
}
|
|
104804
|
+
if (droppedRuns === 0 && sweep)
|
|
104805
|
+
console.log("[gc] No run-level pruning was needed; only the unattributed sweep ran.");
|
|
104194
104806
|
}
|
|
104195
|
-
var _curatorCmdDeps, DEFAULT_KEEP = 50;
|
|
104807
|
+
var _curatorCmdDeps, _testing, DEFAULT_KEEP = 50;
|
|
104196
104808
|
var init_curator2 = __esm(() => {
|
|
104197
104809
|
init_config();
|
|
104198
104810
|
init_heuristics();
|
|
104811
|
+
init_rollup_prune();
|
|
104199
104812
|
init_paths2();
|
|
104200
104813
|
init_common();
|
|
104201
104814
|
_curatorCmdDeps = {
|
|
@@ -104205,6 +104818,9 @@ var init_curator2 = __esm(() => {
|
|
|
104205
104818
|
globalOutputDir: () => globalOutputDir(),
|
|
104206
104819
|
curatorRollupPath: (gDir, override) => curatorRollupPath(gDir, override),
|
|
104207
104820
|
readFile: async (p) => Bun.file(p).text(),
|
|
104821
|
+
fileExists: async (p) => Bun.file(p).exists(),
|
|
104822
|
+
scanProjectRunIds: (rollupPath, projectKey) => scanProjectRunIds(rollupPath, projectKey),
|
|
104823
|
+
pruneRollup: (input) => pruneRollup(input),
|
|
104208
104824
|
writeFile: async (p, content) => {
|
|
104209
104825
|
await Bun.write(p, content);
|
|
104210
104826
|
},
|
|
@@ -104215,7 +104831,7 @@ var init_curator2 = __esm(() => {
|
|
|
104215
104831
|
},
|
|
104216
104832
|
removeFile: async (p) => {
|
|
104217
104833
|
try {
|
|
104218
|
-
await
|
|
104834
|
+
await unlink5(p);
|
|
104219
104835
|
} catch {}
|
|
104220
104836
|
},
|
|
104221
104837
|
openInEditor: async (filePath) => {
|
|
@@ -104226,13 +104842,14 @@ var init_curator2 = __esm(() => {
|
|
|
104226
104842
|
}
|
|
104227
104843
|
}
|
|
104228
104844
|
};
|
|
104845
|
+
_testing = { parseCheckedProposals };
|
|
104229
104846
|
});
|
|
104230
104847
|
|
|
104231
104848
|
// bin/nax.ts
|
|
104232
104849
|
init_source();
|
|
104233
104850
|
import { existsSync as existsSync39, mkdirSync as mkdirSync8 } from "fs";
|
|
104234
104851
|
import { homedir as homedir3 } from "os";
|
|
104235
|
-
import { basename as
|
|
104852
|
+
import { basename as basename20, join as join101 } from "path";
|
|
104236
104853
|
|
|
104237
104854
|
// node_modules/commander/esm.mjs
|
|
104238
104855
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -104889,12 +105506,12 @@ init_errors();
|
|
|
104889
105506
|
init_logger2();
|
|
104890
105507
|
init_runtime();
|
|
104891
105508
|
import { existsSync as existsSync18, readdirSync as readdirSync4 } from "fs";
|
|
104892
|
-
import { basename as
|
|
105509
|
+
import { basename as basename10, join as join46 } from "path";
|
|
104893
105510
|
async function resolveOutputDir(workdir, override) {
|
|
104894
105511
|
if (override)
|
|
104895
105512
|
return override;
|
|
104896
105513
|
const config2 = await loadConfig(workdir).catch(() => null);
|
|
104897
|
-
const projectKey = config2?.name?.trim() ||
|
|
105514
|
+
const projectKey = config2?.name?.trim() || basename10(workdir);
|
|
104898
105515
|
return projectOutputDir(projectKey, config2?.outputDir);
|
|
104899
105516
|
}
|
|
104900
105517
|
async function parseRunLog(logPath) {
|
|
@@ -105929,7 +106546,7 @@ async function contextInspectCommand(options) {
|
|
|
105929
106546
|
init_canonical_loader();
|
|
105930
106547
|
init_errors();
|
|
105931
106548
|
import { mkdir as mkdir11 } from "fs/promises";
|
|
105932
|
-
import { basename as
|
|
106549
|
+
import { basename as basename14, join as join72 } from "path";
|
|
105933
106550
|
var _rulesCLIDeps = {
|
|
105934
106551
|
readFile: async (path24) => Bun.file(path24).text(),
|
|
105935
106552
|
writeFile: async (path24, content) => {
|
|
@@ -106014,6 +106631,32 @@ function neutralizeContent(content) {
|
|
|
106014
106631
|
apply(/\p{Extended_Pictographic}/gu, "");
|
|
106015
106632
|
return { content: result.trim(), replacements };
|
|
106016
106633
|
}
|
|
106634
|
+
function translateLegacyFrontmatter(content) {
|
|
106635
|
+
const fm = /^---\n([\s\S]*?)\n---\n/.exec(content);
|
|
106636
|
+
if (!fm?.[1])
|
|
106637
|
+
return { content, translated: false };
|
|
106638
|
+
const block = fm[1];
|
|
106639
|
+
if (!/^paths:/m.test(block) || /^appliesTo:/m.test(block))
|
|
106640
|
+
return { content, translated: false };
|
|
106641
|
+
const rewritten = block.replace(/^paths:/m, "appliesTo:");
|
|
106642
|
+
const head = content.slice(0, fm.index);
|
|
106643
|
+
const tail = content.slice(fm.index + fm[0].length);
|
|
106644
|
+
return { content: `${head}---
|
|
106645
|
+
${rewritten}
|
|
106646
|
+
---
|
|
106647
|
+
${tail}`, translated: true };
|
|
106648
|
+
}
|
|
106649
|
+
function withReviewNotice(content, replacements) {
|
|
106650
|
+
if (replacements <= 0)
|
|
106651
|
+
return content;
|
|
106652
|
+
const notice = `<!-- NOTE: ${replacements} neutralization(s) applied \u2014 review before committing -->
|
|
106653
|
+
|
|
106654
|
+
`;
|
|
106655
|
+
const fm = /^---\n[\s\S]*?\n---\n/.exec(content);
|
|
106656
|
+
if (!fm)
|
|
106657
|
+
return notice + content;
|
|
106658
|
+
return content.slice(0, fm[0].length) + notice + content.slice(fm[0].length).replace(/^\n+/, "");
|
|
106659
|
+
}
|
|
106017
106660
|
async function collectMigrationSources(workdir) {
|
|
106018
106661
|
const sources = [];
|
|
106019
106662
|
const claudeMdPath = join72(workdir, "CLAUDE.md");
|
|
@@ -106029,7 +106672,7 @@ async function collectMigrationSources(workdir) {
|
|
|
106029
106672
|
try {
|
|
106030
106673
|
const content = await _rulesCLIDeps.readFile(filePath);
|
|
106031
106674
|
if (content.trim()) {
|
|
106032
|
-
sources.push({ sourcePath: filePath, targetFileName:
|
|
106675
|
+
sources.push({ sourcePath: filePath, targetFileName: basename14(filePath), content });
|
|
106033
106676
|
}
|
|
106034
106677
|
} catch {}
|
|
106035
106678
|
}
|
|
@@ -106060,11 +106703,9 @@ async function rulesMigrateCommand(options) {
|
|
|
106060
106703
|
skipped++;
|
|
106061
106704
|
continue;
|
|
106062
106705
|
}
|
|
106063
|
-
const { content:
|
|
106064
|
-
const
|
|
106065
|
-
|
|
106066
|
-
` : "";
|
|
106067
|
-
const output = notice + neutralized;
|
|
106706
|
+
const { content: scoped2 } = translateLegacyFrontmatter(content);
|
|
106707
|
+
const { content: neutralized, replacements } = neutralizeContent(scoped2);
|
|
106708
|
+
const output = withReviewNotice(neutralized, replacements);
|
|
106068
106709
|
if (options.dryRun) {
|
|
106069
106710
|
console.log(`[dry-run] Would write ${targetFileName} from ${sourcePath} (${replacements} replacements)`);
|
|
106070
106711
|
} else {
|
|
@@ -106140,6 +106781,7 @@ async function resolveRunProfileOverride(opts) {
|
|
|
106140
106781
|
}
|
|
106141
106782
|
// src/cli/features-resolve.ts
|
|
106142
106783
|
init_config();
|
|
106784
|
+
init_test_runners();
|
|
106143
106785
|
import { existsSync as existsSync28, readdirSync as readdirSync6 } from "fs";
|
|
106144
106786
|
import { join as join74, relative as relative17 } from "path";
|
|
106145
106787
|
|
|
@@ -106198,6 +106840,15 @@ async function resolveGroupCommand(repoRoot, packageDir, rootCommand) {
|
|
|
106198
106840
|
}
|
|
106199
106841
|
|
|
106200
106842
|
// src/cli/features-resolve.ts
|
|
106843
|
+
async function resolveTestPatterns(workdir) {
|
|
106844
|
+
try {
|
|
106845
|
+
const config2 = await loadConfig(workdir);
|
|
106846
|
+
const resolved = await resolveTestFilePatterns(config2, workdir);
|
|
106847
|
+
return { regex: resolved.regex.map((r) => r.source), resolution: resolved.resolution };
|
|
106848
|
+
} catch {
|
|
106849
|
+
return;
|
|
106850
|
+
}
|
|
106851
|
+
}
|
|
106201
106852
|
async function isNonEmptyFile(absolutePath) {
|
|
106202
106853
|
if (!existsSync28(absolutePath))
|
|
106203
106854
|
return false;
|
|
@@ -106302,6 +106953,7 @@ async function resolveFeatureSpec(name, workdir) {
|
|
|
106302
106953
|
featureName: name,
|
|
106303
106954
|
specSource: source2,
|
|
106304
106955
|
acceptance: await resolveFeatureAcceptance(name, workdir),
|
|
106956
|
+
testPatterns: await resolveTestPatterns(workdir),
|
|
106305
106957
|
message: `resolved spec: ${source2.path}`
|
|
106306
106958
|
};
|
|
106307
106959
|
}
|
|
@@ -106348,6 +107000,7 @@ async function resolveFeatureSpec(name, workdir) {
|
|
|
106348
107000
|
featureName: onlyName,
|
|
106349
107001
|
specSource: source,
|
|
106350
107002
|
acceptance: await resolveFeatureAcceptance(onlyName, workdir),
|
|
107003
|
+
testPatterns: await resolveTestPatterns(workdir),
|
|
106351
107004
|
message: `resolved spec: ${source.path}`
|
|
106352
107005
|
};
|
|
106353
107006
|
}
|
|
@@ -106367,7 +107020,7 @@ init_runtime();
|
|
|
106367
107020
|
init_json_file();
|
|
106368
107021
|
init_routing();
|
|
106369
107022
|
import { mkdirSync as mkdirSync6 } from "fs";
|
|
106370
|
-
import { basename as
|
|
107023
|
+
import { basename as basename15, join as join75 } from "path";
|
|
106371
107024
|
var _routingCalibrateDeps = {
|
|
106372
107025
|
loadRunMetrics: (outputDir) => loadRunMetrics(outputDir),
|
|
106373
107026
|
readConfig: (workdir) => loadConfig(workdir),
|
|
@@ -106425,7 +107078,7 @@ async function runRoutingCalibrateCli(options, deps = _routingCalibrateDeps) {
|
|
|
106425
107078
|
function resolveOutputDir2(workdir, override, prior) {
|
|
106426
107079
|
if (override)
|
|
106427
107080
|
return override;
|
|
106428
|
-
const key = prior?.name?.trim() ||
|
|
107081
|
+
const key = prior?.name?.trim() || basename15(workdir);
|
|
106429
107082
|
return projectOutputDir(key, prior?.outputDir);
|
|
106430
107083
|
}
|
|
106431
107084
|
function mergeComplexityRouting(prior, adjustments) {
|
|
@@ -107411,7 +108064,7 @@ init_errors();
|
|
|
107411
108064
|
init_checkpoint();
|
|
107412
108065
|
init_runtime();
|
|
107413
108066
|
import { existsSync as existsSync37 } from "fs";
|
|
107414
|
-
import { basename as
|
|
108067
|
+
import { basename as basename18, join as join95 } from "path";
|
|
107415
108068
|
async function defaultCheckpointExists(featureDir) {
|
|
107416
108069
|
if (!featureDir || !existsSync37(featureDir))
|
|
107417
108070
|
return false;
|
|
@@ -107490,7 +108143,7 @@ function registerResumeCommand(program2) {
|
|
|
107490
108143
|
const globalNaxDir = globalConfigDir();
|
|
107491
108144
|
const hooks = await loadHooksConfig2(naxDir, globalNaxDir);
|
|
107492
108145
|
applyResumeModeDeps2(opts.featureDir ?? "", "auto");
|
|
107493
|
-
const projectKey = config2.name?.trim() ||
|
|
108146
|
+
const projectKey = config2.name?.trim() || basename18(cmdOpts.dir);
|
|
107494
108147
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
107495
108148
|
const statusFilePath = join95(outputDir, "status.json");
|
|
107496
108149
|
const result = await run2({
|
|
@@ -115941,7 +116594,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
115941
116594
|
process.exit(1);
|
|
115942
116595
|
}
|
|
115943
116596
|
resetLogger();
|
|
115944
|
-
const projectKey = config2.name?.trim() ||
|
|
116597
|
+
const projectKey = config2.name?.trim() || basename20(workdir);
|
|
115945
116598
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
115946
116599
|
const runsDir = join101(outputDir, "features", options.feature, "runs");
|
|
115947
116600
|
mkdirSync8(runsDir, { recursive: true });
|
|
@@ -116562,12 +117215,13 @@ curator.command("dryrun").description("Re-run heuristics on an existing observat
|
|
|
116562
117215
|
process.exit(1);
|
|
116563
117216
|
}
|
|
116564
117217
|
});
|
|
116565
|
-
curator.command("gc").description("Prune old rows from the curator rollup JSONL").option("-p, --project <path>", "Project directory (default: CWD)").option("--keep <N>", "Number of most recent runIds to keep (default: 50)", "50").action(async (options) => {
|
|
117218
|
+
curator.command("gc").description("Prune old rows from the curator rollup JSONL").option("-p, --project <path>", "Project directory (default: CWD)").option("--keep <N>", "Number of most recent runIds to keep (default: 50)", "50").option("--sweep-unattributed", "Also drop rows with no projectKey (pre-#1429 history, unreadable by any project). Machine-wide \u2014 affects every project sharing the rollup.").action(async (options) => {
|
|
116566
117219
|
const { curatorGc: curatorGc2 } = await Promise.resolve().then(() => (init_curator2(), exports_curator));
|
|
116567
117220
|
try {
|
|
116568
117221
|
await curatorGc2({
|
|
116569
117222
|
project: options.project,
|
|
116570
|
-
keep: options.keep !== undefined ? Number.parseInt(options.keep, 10) : undefined
|
|
117223
|
+
keep: options.keep !== undefined ? Number.parseInt(options.keep, 10) : undefined,
|
|
117224
|
+
sweepUnattributed: options.sweepUnattributed === true
|
|
116571
117225
|
});
|
|
116572
117226
|
} catch (err) {
|
|
116573
117227
|
console.error(source_default.red(`Error: ${err.message}`));
|