@nathapp/nax 0.82.0-canary.6 → 0.82.0-canary.7
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 +761 -529
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -22256,7 +22256,8 @@ var init_schemas_infra = __esm(() => {
|
|
|
22256
22256
|
});
|
|
22257
22257
|
FallbackTargetSchema = exports_external.union([
|
|
22258
22258
|
exports_external.string().min(1),
|
|
22259
|
-
exports_external.object({ agent: exports_external.string().min(1), tier: exports_external.string().min(1) })
|
|
22259
|
+
exports_external.object({ agent: exports_external.string().min(1), tier: exports_external.string().min(1) }),
|
|
22260
|
+
exports_external.object({ agent: exports_external.string().min(1), model: exports_external.string().min(1) })
|
|
22260
22261
|
]);
|
|
22261
22262
|
AgentFallbackConfigSchema = exports_external.object({
|
|
22262
22263
|
enabled: exports_external.boolean().default(false),
|
|
@@ -29993,36 +29994,76 @@ function resolveCooldownExpiry(failure, now) {
|
|
|
29993
29994
|
var TRANSIENT_COOLDOWN_MS = 60000, POLICIES;
|
|
29994
29995
|
var init_failure_policy = __esm(() => {
|
|
29995
29996
|
POLICIES = Object.freeze({
|
|
29996
|
-
"fail-auth": {
|
|
29997
|
-
|
|
29997
|
+
"fail-auth": {
|
|
29998
|
+
sameAgentRetry: "none",
|
|
29999
|
+
swap: "immediate",
|
|
30000
|
+
cooldown: "run",
|
|
30001
|
+
cooldownScope: "agent",
|
|
30002
|
+
terminalBackoff: false
|
|
30003
|
+
},
|
|
30004
|
+
"fail-quota": {
|
|
30005
|
+
sameAgentRetry: "none",
|
|
30006
|
+
swap: "immediate",
|
|
30007
|
+
cooldown: "run",
|
|
30008
|
+
cooldownScope: "agent",
|
|
30009
|
+
terminalBackoff: false
|
|
30010
|
+
},
|
|
29998
30011
|
"fail-rate-limit": {
|
|
29999
30012
|
sameAgentRetry: "none",
|
|
30000
30013
|
swap: "immediate",
|
|
30001
30014
|
cooldown: { ms: TRANSIENT_COOLDOWN_MS },
|
|
30015
|
+
cooldownScope: "model",
|
|
30002
30016
|
terminalBackoff: true
|
|
30003
30017
|
},
|
|
30004
30018
|
"fail-service-down": {
|
|
30005
30019
|
sameAgentRetry: "adapter-error",
|
|
30006
30020
|
swap: "immediate",
|
|
30007
30021
|
cooldown: { ms: TRANSIENT_COOLDOWN_MS },
|
|
30022
|
+
cooldownScope: "model",
|
|
30008
30023
|
terminalBackoff: true
|
|
30009
30024
|
},
|
|
30010
30025
|
"fail-stale": {
|
|
30011
30026
|
sameAgentRetry: "stale",
|
|
30012
30027
|
swap: "immediate",
|
|
30013
30028
|
cooldown: { ms: TRANSIENT_COOLDOWN_MS },
|
|
30029
|
+
cooldownScope: "model",
|
|
30014
30030
|
terminalBackoff: true
|
|
30015
30031
|
},
|
|
30016
|
-
"fail-timeout": {
|
|
30032
|
+
"fail-timeout": {
|
|
30033
|
+
sameAgentRetry: "timeout",
|
|
30034
|
+
swap: "after-retry-lane",
|
|
30035
|
+
cooldown: "none",
|
|
30036
|
+
cooldownScope: "model",
|
|
30037
|
+
terminalBackoff: false
|
|
30038
|
+
},
|
|
30017
30039
|
"fail-adapter-error": {
|
|
30018
30040
|
sameAgentRetry: "adapter-error",
|
|
30019
30041
|
swap: "quality-gated",
|
|
30020
30042
|
cooldown: "none",
|
|
30043
|
+
cooldownScope: "model",
|
|
30044
|
+
terminalBackoff: false
|
|
30045
|
+
},
|
|
30046
|
+
"fail-quality": {
|
|
30047
|
+
sameAgentRetry: "none",
|
|
30048
|
+
swap: "quality-gated",
|
|
30049
|
+
cooldown: "none",
|
|
30050
|
+
cooldownScope: "model",
|
|
30051
|
+
terminalBackoff: false
|
|
30052
|
+
},
|
|
30053
|
+
"fail-unknown": {
|
|
30054
|
+
sameAgentRetry: "none",
|
|
30055
|
+
swap: "quality-gated",
|
|
30056
|
+
cooldown: "none",
|
|
30057
|
+
cooldownScope: "model",
|
|
30021
30058
|
terminalBackoff: false
|
|
30022
30059
|
},
|
|
30023
|
-
"fail-
|
|
30024
|
-
|
|
30025
|
-
|
|
30060
|
+
"fail-aborted": {
|
|
30061
|
+
sameAgentRetry: "none",
|
|
30062
|
+
swap: "never",
|
|
30063
|
+
cooldown: "none",
|
|
30064
|
+
cooldownScope: "model",
|
|
30065
|
+
terminalBackoff: false
|
|
30066
|
+
}
|
|
30026
30067
|
});
|
|
30027
30068
|
});
|
|
30028
30069
|
|
|
@@ -30033,43 +30074,81 @@ class CooldownStore {
|
|
|
30033
30074
|
constructor(_now) {
|
|
30034
30075
|
this._now = _now;
|
|
30035
30076
|
}
|
|
30036
|
-
mark(agent, failure) {
|
|
30077
|
+
mark(agent, failure, tier, modelId) {
|
|
30037
30078
|
const expiresAt = resolveCooldownExpiry(failure, this._now());
|
|
30038
30079
|
if (expiresAt === null)
|
|
30039
30080
|
return;
|
|
30040
|
-
|
|
30081
|
+
const key = failurePolicyFor(failure.outcome).cooldownScope === "agent" ? agent : identityKey(agent, tier, modelId);
|
|
30082
|
+
this._entries.set(key, { failure, expiresAt });
|
|
30041
30083
|
}
|
|
30042
|
-
isCooling(agent) {
|
|
30043
|
-
return this._live(agent) !== undefined;
|
|
30084
|
+
isCooling(agent, tier, modelId) {
|
|
30085
|
+
return this._live(agent, tier, modelId) !== undefined;
|
|
30044
30086
|
}
|
|
30045
|
-
failureFor(agent) {
|
|
30046
|
-
return this._live(agent)?.failure;
|
|
30087
|
+
failureFor(agent, tier, modelId) {
|
|
30088
|
+
return this._live(agent, tier, modelId)?.failure;
|
|
30047
30089
|
}
|
|
30048
30090
|
sweepTransient() {
|
|
30049
|
-
for (const [
|
|
30091
|
+
for (const [key, entry] of this._entries) {
|
|
30050
30092
|
if (entry.expiresAt !== "run")
|
|
30051
|
-
this._entries.delete(
|
|
30093
|
+
this._entries.delete(key);
|
|
30052
30094
|
}
|
|
30053
30095
|
}
|
|
30054
30096
|
clear() {
|
|
30055
30097
|
this._entries.clear();
|
|
30056
30098
|
}
|
|
30057
|
-
_live(agent) {
|
|
30058
|
-
const
|
|
30099
|
+
_live(agent, tier, modelId) {
|
|
30100
|
+
const narrow = identityKey(agent, tier, modelId);
|
|
30101
|
+
const own = this._at(narrow);
|
|
30102
|
+
if (own || narrow === agent)
|
|
30103
|
+
return own;
|
|
30104
|
+
const agentWide = this._at(agent);
|
|
30105
|
+
return agentWide && failurePolicyFor(agentWide.failure.outcome).cooldownScope === "agent" ? agentWide : undefined;
|
|
30106
|
+
}
|
|
30107
|
+
_at(key) {
|
|
30108
|
+
const entry = this._entries.get(key);
|
|
30059
30109
|
if (!entry)
|
|
30060
30110
|
return;
|
|
30061
30111
|
if (entry.expiresAt === "run")
|
|
30062
30112
|
return entry;
|
|
30063
30113
|
if (entry.expiresAt > this._now())
|
|
30064
30114
|
return entry;
|
|
30065
|
-
this._entries.delete(
|
|
30115
|
+
this._entries.delete(key);
|
|
30066
30116
|
return;
|
|
30067
30117
|
}
|
|
30068
30118
|
}
|
|
30119
|
+
function identityKey(agent, tier, modelId) {
|
|
30120
|
+
if (modelId)
|
|
30121
|
+
return `${agent}::model::${modelId}`;
|
|
30122
|
+
return tier ? `${agent}::${tier}` : agent;
|
|
30123
|
+
}
|
|
30069
30124
|
var init_cooldown_store = __esm(() => {
|
|
30070
30125
|
init_failure_policy();
|
|
30071
30126
|
});
|
|
30072
30127
|
|
|
30128
|
+
// src/agents/fallback-model-identity.ts
|
|
30129
|
+
function resolveFallbackModelId(models, agent, tier, defaultAgent) {
|
|
30130
|
+
if (!tier || !models)
|
|
30131
|
+
return;
|
|
30132
|
+
try {
|
|
30133
|
+
const def = resolveModelForAgent(models, agent, tier, defaultAgent);
|
|
30134
|
+
return `${def.provider}/${def.model}`;
|
|
30135
|
+
} catch {
|
|
30136
|
+
return;
|
|
30137
|
+
}
|
|
30138
|
+
}
|
|
30139
|
+
function resolveFallbackDispatchTarget(models, defaultAgent, target) {
|
|
30140
|
+
if (target.model === undefined || !models)
|
|
30141
|
+
return target;
|
|
30142
|
+
const aliased = MODEL_SHORTHAND_TIERS[target.model.toLowerCase()] ?? target.model;
|
|
30143
|
+
const membership = resolveTierMembership(models, target.agent, aliased, defaultAgent);
|
|
30144
|
+
if (membership.isTier)
|
|
30145
|
+
return { agent: target.agent, tier: aliased };
|
|
30146
|
+
return target;
|
|
30147
|
+
}
|
|
30148
|
+
var init_fallback_model_identity = __esm(() => {
|
|
30149
|
+
init_config();
|
|
30150
|
+
});
|
|
30151
|
+
|
|
30073
30152
|
// src/agents/hop-budget.ts
|
|
30074
30153
|
function resolveStartAgent(source, primary, fallbackEnabled, storyId, logger) {
|
|
30075
30154
|
if (!fallbackEnabled || !source.isUnavailable(primary))
|
|
@@ -30247,9 +30326,11 @@ function buildCompleteCallPreamble(input) {
|
|
|
30247
30326
|
})
|
|
30248
30327
|
};
|
|
30249
30328
|
}
|
|
30250
|
-
function resolveHopCompleteOptions(options, currentAgent, primaryAgent, tier) {
|
|
30329
|
+
function resolveHopCompleteOptions(options, currentAgent, primaryAgent, tier, model) {
|
|
30251
30330
|
if (currentAgent === primaryAgent)
|
|
30252
30331
|
return options;
|
|
30332
|
+
if (model !== undefined)
|
|
30333
|
+
return { ...options, modelDef: resolveModel(model) };
|
|
30253
30334
|
return { ...options, modelDef: options.modelDefFor?.(currentAgent, tier) ?? options.modelDef };
|
|
30254
30335
|
}
|
|
30255
30336
|
function resolveFinalDispatch(options, primaryAgent, fallbacks, finalTier) {
|
|
@@ -30324,7 +30405,7 @@ var init_manager_exhaustion = __esm(() => {
|
|
|
30324
30405
|
|
|
30325
30406
|
// src/agents/retry/hop-retry-policy.ts
|
|
30326
30407
|
function trySameAgentRetry(result, state, deps) {
|
|
30327
|
-
const { staleRetryAttempts, timeoutRetryAttempts, adapterErrorRetries, currentRunOptions, tier } = state;
|
|
30408
|
+
const { staleRetryAttempts, timeoutRetryAttempts, adapterErrorRetries, currentRunOptions, tier, model } = state;
|
|
30328
30409
|
const { config: config2, requestRunOptions, signal } = deps;
|
|
30329
30410
|
const outcome = result.adapterFailure?.outcome;
|
|
30330
30411
|
const lane = outcome ? failurePolicyFor(outcome).sameAgentRetry : "none";
|
|
@@ -30335,7 +30416,12 @@ function trySameAgentRetry(result, state, deps) {
|
|
|
30335
30416
|
return {
|
|
30336
30417
|
outcome: "stale-retry",
|
|
30337
30418
|
staleRetryAttempts: newAttempts,
|
|
30338
|
-
kind: {
|
|
30419
|
+
kind: {
|
|
30420
|
+
kind: "stale-retry",
|
|
30421
|
+
attempt: newAttempts,
|
|
30422
|
+
...tier !== undefined ? { tier } : {},
|
|
30423
|
+
...model !== undefined ? { model } : {}
|
|
30424
|
+
},
|
|
30339
30425
|
fallbackRecord: {
|
|
30340
30426
|
outcome: result.adapterFailure?.outcome ?? "fail-stale",
|
|
30341
30427
|
category: result.adapterFailure?.category ?? "availability",
|
|
@@ -30352,7 +30438,12 @@ function trySameAgentRetry(result, state, deps) {
|
|
|
30352
30438
|
return {
|
|
30353
30439
|
outcome: "timeout-retry",
|
|
30354
30440
|
timeoutRetryAttempts: newAttempts,
|
|
30355
|
-
kind: {
|
|
30441
|
+
kind: {
|
|
30442
|
+
kind: "timeout-retry",
|
|
30443
|
+
attempt: newAttempts,
|
|
30444
|
+
...tier !== undefined ? { tier } : {},
|
|
30445
|
+
...model !== undefined ? { model } : {}
|
|
30446
|
+
},
|
|
30356
30447
|
currentRunOptions: resolveTimeoutRetryOptions(currentRunOptions, timeoutConfig, config2.execution, requestRunOptions),
|
|
30357
30448
|
fallbackRecord: {
|
|
30358
30449
|
outcome: result.adapterFailure?.outcome ?? "fail-timeout",
|
|
@@ -30372,7 +30463,12 @@ function trySameAgentRetry(result, state, deps) {
|
|
|
30372
30463
|
return {
|
|
30373
30464
|
outcome: "adapter-error",
|
|
30374
30465
|
adapterErrorRetries: newAttempts,
|
|
30375
|
-
kind: {
|
|
30466
|
+
kind: {
|
|
30467
|
+
kind: "stale-retry",
|
|
30468
|
+
attempt: newAttempts,
|
|
30469
|
+
...tier !== undefined ? { tier } : {},
|
|
30470
|
+
...model !== undefined ? { model } : {}
|
|
30471
|
+
},
|
|
30376
30472
|
fallbackRecord: {
|
|
30377
30473
|
outcome: result.adapterFailure?.outcome ?? "fail-adapter-error",
|
|
30378
30474
|
category: result.adapterFailure?.category ?? "availability",
|
|
@@ -30447,10 +30543,14 @@ function decideSwap(failure, hopsSoFar, fallback) {
|
|
|
30447
30543
|
return { swap: true };
|
|
30448
30544
|
}
|
|
30449
30545
|
function normaliseFallbackTarget(value) {
|
|
30450
|
-
|
|
30546
|
+
if (typeof value === "string")
|
|
30547
|
+
return { agent: value };
|
|
30548
|
+
if ("tier" in value)
|
|
30549
|
+
return { agent: value.agent, tier: value.tier };
|
|
30550
|
+
return { agent: value.agent, model: value.model };
|
|
30451
30551
|
}
|
|
30452
|
-
function availableCandidates(map2, agent, isExcluded) {
|
|
30453
|
-
return (map2?.[agent] ?? []).map(normaliseFallbackTarget).filter((candidate) => !isExcluded(candidate.agent));
|
|
30552
|
+
function availableCandidates(map2, agent, isExcluded, resolve9 = (target) => target) {
|
|
30553
|
+
return (map2?.[agent] ?? []).map(normaliseFallbackTarget).map(resolve9).filter((candidate) => !isExcluded(candidate.agent, candidate.tier));
|
|
30454
30554
|
}
|
|
30455
30555
|
function credentialCandidates(map2, primary) {
|
|
30456
30556
|
const candidates = new Set([primary]);
|
|
@@ -30484,7 +30584,11 @@ async function runWithFallback(input) {
|
|
|
30484
30584
|
const storyId = request.runOptions.storyId;
|
|
30485
30585
|
const start = resolveStartAgent(input, primaryAgent, config2.agent?.fallback?.enabled, storyId, logger);
|
|
30486
30586
|
let currentAgent = start.agent;
|
|
30487
|
-
let currentHopKind =
|
|
30587
|
+
let currentHopKind = {
|
|
30588
|
+
kind: "primary",
|
|
30589
|
+
..."tier" in start && start.tier !== undefined ? { tier: start.tier } : {},
|
|
30590
|
+
..."model" in start && start.model !== undefined ? { model: start.model } : {}
|
|
30591
|
+
};
|
|
30488
30592
|
let hopsSoFar = budget.spent(storyId);
|
|
30489
30593
|
let rateLimitRetry = 0;
|
|
30490
30594
|
let staleRetryAttempts = 0;
|
|
@@ -30508,7 +30612,14 @@ async function runWithFallback(input) {
|
|
|
30508
30612
|
finalStatus = "ok";
|
|
30509
30613
|
return { result, fallbacks, finalBundle: updatedBundle, finalPrompt, finalAgent: currentAgent };
|
|
30510
30614
|
}
|
|
30511
|
-
const retry = trySameAgentRetry(result, {
|
|
30615
|
+
const retry = trySameAgentRetry(result, {
|
|
30616
|
+
staleRetryAttempts,
|
|
30617
|
+
timeoutRetryAttempts,
|
|
30618
|
+
adapterErrorRetries,
|
|
30619
|
+
currentRunOptions,
|
|
30620
|
+
tier: currentHopKind.tier,
|
|
30621
|
+
model: currentHopKind.model
|
|
30622
|
+
}, { config: config2, requestRunOptions: request.runOptions, signal: request.signal });
|
|
30512
30623
|
if (retry) {
|
|
30513
30624
|
({ staleRetryAttempts, timeoutRetryAttempts, adapterErrorRetries, currentRunOptions } = applyRetry(retry, {
|
|
30514
30625
|
staleRetryAttempts,
|
|
@@ -30556,8 +30667,9 @@ async function runWithFallback(input) {
|
|
|
30556
30667
|
return { result, fallbacks, finalBundle: updatedBundle, finalPrompt, finalAgent: currentAgent };
|
|
30557
30668
|
}
|
|
30558
30669
|
const failure = result.adapterFailure ?? unknownFailure();
|
|
30559
|
-
|
|
30560
|
-
|
|
30670
|
+
const currentTier = currentHopKind.tier;
|
|
30671
|
+
input.markUnavailable(currentAgent, failure, currentTier);
|
|
30672
|
+
const next = input.nextCandidate(primaryAgent, hopsSoFar, currentAgent, currentTier);
|
|
30561
30673
|
if (!next) {
|
|
30562
30674
|
const outcome = await input.resolveExhaustion({
|
|
30563
30675
|
failure,
|
|
@@ -30580,7 +30692,12 @@ async function runWithFallback(input) {
|
|
|
30580
30692
|
hopsSoFar = budget.spend(storyId, hopsSoFar);
|
|
30581
30693
|
rateLimitRetry = 0;
|
|
30582
30694
|
currentBundle = updatedBundle;
|
|
30583
|
-
currentHopKind = {
|
|
30695
|
+
currentHopKind = {
|
|
30696
|
+
kind: "swap",
|
|
30697
|
+
failure,
|
|
30698
|
+
...next.tier ? { tier: next.tier } : {},
|
|
30699
|
+
...next.model ? { model: next.model } : {}
|
|
30700
|
+
};
|
|
30584
30701
|
const fallback = buildFallbackRecord({
|
|
30585
30702
|
storyId,
|
|
30586
30703
|
priorAgent: currentAgent,
|
|
@@ -30780,6 +30897,7 @@ class AgentManager {
|
|
|
30780
30897
|
_dispatchEvents;
|
|
30781
30898
|
_pidRegistry;
|
|
30782
30899
|
_retryStrategy;
|
|
30900
|
+
_models;
|
|
30783
30901
|
events;
|
|
30784
30902
|
constructor(config2, registry3, opts) {
|
|
30785
30903
|
this._config = config2;
|
|
@@ -30792,6 +30910,7 @@ class AgentManager {
|
|
|
30792
30910
|
this._runHop = opts?.runHop;
|
|
30793
30911
|
this._dispatchEvents = opts?.dispatchEvents ?? new DispatchEventBus;
|
|
30794
30912
|
this._retryStrategy = opts?.retryStrategy ?? defaultRetryStrategy;
|
|
30913
|
+
this._models = opts?.models;
|
|
30795
30914
|
this.events = {
|
|
30796
30915
|
on: (event, listener) => {
|
|
30797
30916
|
this._emitter.on(event, listener);
|
|
@@ -30818,12 +30937,12 @@ class AgentManager {
|
|
|
30818
30937
|
return fromAgent;
|
|
30819
30938
|
return "claude";
|
|
30820
30939
|
}
|
|
30821
|
-
isUnavailable(agent) {
|
|
30822
|
-
return this._cooldowns.isCooling(agent);
|
|
30940
|
+
isUnavailable(agent, tier) {
|
|
30941
|
+
return this._cooldowns.isCooling(agent, tier, resolveFallbackModelId(this._models, agent, tier, this.getDefault()));
|
|
30823
30942
|
}
|
|
30824
|
-
markUnavailable(agent, reason) {
|
|
30825
|
-
this._cooldowns.mark(agent, reason);
|
|
30826
|
-
this._emitter.emit("onAgentUnavailable", { agent, failure: reason });
|
|
30943
|
+
markUnavailable(agent, reason, tier) {
|
|
30944
|
+
this._cooldowns.mark(agent, reason, tier, resolveFallbackModelId(this._models, agent, tier, this.getDefault()));
|
|
30945
|
+
this._emitter.emit("onAgentUnavailable", { agent, tier, failure: reason });
|
|
30827
30946
|
}
|
|
30828
30947
|
reset() {
|
|
30829
30948
|
this._cooldowns.clear();
|
|
@@ -30855,16 +30974,18 @@ class AgentManager {
|
|
|
30855
30974
|
this._prunedFallback.add(name);
|
|
30856
30975
|
}
|
|
30857
30976
|
}
|
|
30858
|
-
_isExcluded = (c) => this._prunedFallback.has(c) || this.isUnavailable(c);
|
|
30977
|
+
_isExcluded = (c, t) => this._prunedFallback.has(c) || this.isUnavailable(c, t);
|
|
30978
|
+
_resolveTarget = (t) => resolveFallbackDispatchTarget(this._models, this.getDefault(), t);
|
|
30859
30979
|
resolveFallbackChain(agent, _failure) {
|
|
30860
|
-
return availableCandidates(this._config.agent?.fallback?.map, agent, this._isExcluded);
|
|
30980
|
+
return availableCandidates(this._config.agent?.fallback?.map, agent, this._isExcluded, this._resolveTarget);
|
|
30861
30981
|
}
|
|
30862
30982
|
shouldSwap(failure, hopsSoFar) {
|
|
30863
30983
|
return decideSwap(failure, hopsSoFar, this._config.agent?.fallback).swap;
|
|
30864
30984
|
}
|
|
30865
|
-
nextCandidate(current, _hopsSoFar, exclude) {
|
|
30866
|
-
const excluded = (
|
|
30867
|
-
|
|
30985
|
+
nextCandidate(current, _hopsSoFar, exclude, excludeTier) {
|
|
30986
|
+
const excluded = (c, t) => c === exclude && t === excludeTier || this._isExcluded(c, t);
|
|
30987
|
+
const map2 = this._config.agent?.fallback?.map;
|
|
30988
|
+
return availableCandidates(map2, current, excluded, this._resolveTarget)[0] ?? null;
|
|
30868
30989
|
}
|
|
30869
30990
|
async runWithFallback(request, primaryAgentOverride) {
|
|
30870
30991
|
return runWithFallback({
|
|
@@ -30876,9 +30997,9 @@ class AgentManager {
|
|
|
30876
30997
|
dispatchEvents: this._dispatchEvents,
|
|
30877
30998
|
logger: this._loggerOverride ?? getSafeLogger(),
|
|
30878
30999
|
getDefault: () => this.getDefault(),
|
|
30879
|
-
isUnavailable: (agent) => this.isUnavailable(agent),
|
|
30880
|
-
markUnavailable: (agent, failure) => this.markUnavailable(agent, failure),
|
|
30881
|
-
nextCandidate: (current, hops, exclude) => this.nextCandidate(current, hops, exclude),
|
|
31000
|
+
isUnavailable: (agent, tier) => this.isUnavailable(agent, tier),
|
|
31001
|
+
markUnavailable: (agent, failure, tier) => this.markUnavailable(agent, failure, tier),
|
|
31002
|
+
nextCandidate: (current, hops, exclude, excludeTier) => this.nextCandidate(current, hops, exclude, excludeTier),
|
|
30882
31003
|
resolveExhaustion: (options) => this._resolveExhaustion(options),
|
|
30883
31004
|
emitSwapAttempt: (fallback) => this._emitter.emit("onSwapAttempt", fallback)
|
|
30884
31005
|
});
|
|
@@ -30889,6 +31010,7 @@ class AgentManager {
|
|
|
30889
31010
|
const primaryAgent = primaryAgentOverride ?? this.getDefault();
|
|
30890
31011
|
let currentAgent = primaryAgent;
|
|
30891
31012
|
let currentTier;
|
|
31013
|
+
let currentModel;
|
|
30892
31014
|
let hopsSoFar = this._budget.spent(options.storyId);
|
|
30893
31015
|
let staleRetryAttempts = 0;
|
|
30894
31016
|
let rateLimitRetry = 0;
|
|
@@ -30899,7 +31021,7 @@ class AgentManager {
|
|
|
30899
31021
|
let _totalCostUsd = 0;
|
|
30900
31022
|
try {
|
|
30901
31023
|
while (true) {
|
|
30902
|
-
const hopOptions = resolveHopCompleteOptions(options, currentAgent, primaryAgent, currentTier);
|
|
31024
|
+
const hopOptions = resolveHopCompleteOptions(options, currentAgent, primaryAgent, currentTier, currentModel);
|
|
30903
31025
|
const adapter = this._resolveRegistry().getAgent(currentAgent);
|
|
30904
31026
|
if (!adapter) {
|
|
30905
31027
|
_finalStatus = "error";
|
|
@@ -30994,8 +31116,8 @@ class AgentManager {
|
|
|
30994
31116
|
_finalStatus = hopsSoFar > 0 ? "exhausted" : "error";
|
|
30995
31117
|
return { result, fallbacks, ...currentTier !== undefined ? { finalTier: currentTier } : {} };
|
|
30996
31118
|
}
|
|
30997
|
-
this.markUnavailable(currentAgent, result.adapterFailure);
|
|
30998
|
-
const next = this.nextCandidate(primaryAgent, hopsSoFar, currentAgent);
|
|
31119
|
+
this.markUnavailable(currentAgent, result.adapterFailure, currentTier);
|
|
31120
|
+
const next = this.nextCandidate(primaryAgent, hopsSoFar, currentAgent, currentTier);
|
|
30999
31121
|
if (!next) {
|
|
31000
31122
|
const outcome = await this._resolveExhaustion({
|
|
31001
31123
|
failure: result.adapterFailure,
|
|
@@ -31037,7 +31159,7 @@ class AgentManager {
|
|
|
31037
31159
|
hop: hopsSoFar
|
|
31038
31160
|
});
|
|
31039
31161
|
_agentChain.push(next.agent);
|
|
31040
|
-
[currentAgent, currentTier] = [next.agent, next.tier];
|
|
31162
|
+
[currentAgent, currentTier, currentModel] = [next.agent, next.tier, next.model];
|
|
31041
31163
|
}
|
|
31042
31164
|
} finally {
|
|
31043
31165
|
this._dispatchEvents.emitOperationCompleted({
|
|
@@ -31218,6 +31340,7 @@ var init_manager = __esm(() => {
|
|
|
31218
31340
|
init_bun_deps();
|
|
31219
31341
|
init_complete_exception_classifier();
|
|
31220
31342
|
init_cooldown_store();
|
|
31343
|
+
init_fallback_model_identity();
|
|
31221
31344
|
init_manager_dispatch();
|
|
31222
31345
|
init_manager_exhaustion();
|
|
31223
31346
|
init_manager_run_fallback();
|
|
@@ -45070,6 +45193,292 @@ function computePollutionMetrics(manifests) {
|
|
|
45070
45193
|
};
|
|
45071
45194
|
}
|
|
45072
45195
|
|
|
45196
|
+
// src/runtime/cost-aggregator.ts
|
|
45197
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
45198
|
+
import { join as join22 } from "path";
|
|
45199
|
+
function totalSpendUsd(snap) {
|
|
45200
|
+
return snap.totalCostUsd + snap.totalErrorCostUsd;
|
|
45201
|
+
}
|
|
45202
|
+
function storySpendUsd(costAggregator, storyId, fallbackUsd) {
|
|
45203
|
+
const snap = costAggregator?.byStory()[storyId];
|
|
45204
|
+
if (snap === undefined)
|
|
45205
|
+
return { cost: fallbackUsd, errorCostUsd: 0 };
|
|
45206
|
+
return { cost: totalSpendUsd(snap), errorCostUsd: snap.totalErrorCostUsd };
|
|
45207
|
+
}
|
|
45208
|
+
function makeCorrelationId() {
|
|
45209
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
45210
|
+
}
|
|
45211
|
+
function createNoOpCostAggregator() {
|
|
45212
|
+
return {
|
|
45213
|
+
record() {},
|
|
45214
|
+
recordError() {},
|
|
45215
|
+
recordOperationSummary() {},
|
|
45216
|
+
snapshot() {
|
|
45217
|
+
return EMPTY_SNAPSHOT;
|
|
45218
|
+
},
|
|
45219
|
+
byAgent() {
|
|
45220
|
+
return {};
|
|
45221
|
+
},
|
|
45222
|
+
byStage() {
|
|
45223
|
+
return {};
|
|
45224
|
+
},
|
|
45225
|
+
byStory() {
|
|
45226
|
+
return {};
|
|
45227
|
+
},
|
|
45228
|
+
byCall() {
|
|
45229
|
+
return {};
|
|
45230
|
+
},
|
|
45231
|
+
byScope() {
|
|
45232
|
+
return {};
|
|
45233
|
+
},
|
|
45234
|
+
openScope(scopeId) {
|
|
45235
|
+
return {
|
|
45236
|
+
scopeId: scopeId ?? makeCorrelationId(),
|
|
45237
|
+
snapshot: () => EMPTY_SNAPSHOT,
|
|
45238
|
+
close: () => {}
|
|
45239
|
+
};
|
|
45240
|
+
},
|
|
45241
|
+
async drain() {}
|
|
45242
|
+
};
|
|
45243
|
+
}
|
|
45244
|
+
function emptySnap() {
|
|
45245
|
+
return {
|
|
45246
|
+
totalCostUsd: 0,
|
|
45247
|
+
totalEstimatedCostUsd: 0,
|
|
45248
|
+
totalExactCostUsd: 0,
|
|
45249
|
+
totalInputTokens: 0,
|
|
45250
|
+
totalOutputTokens: 0,
|
|
45251
|
+
totalCacheReadTokens: 0,
|
|
45252
|
+
totalCacheWriteTokens: 0,
|
|
45253
|
+
tokenUsageCount: 0,
|
|
45254
|
+
callCount: 0,
|
|
45255
|
+
errorCount: 0,
|
|
45256
|
+
totalErrorCostUsd: 0
|
|
45257
|
+
};
|
|
45258
|
+
}
|
|
45259
|
+
function accumulate(snap, e) {
|
|
45260
|
+
const tokens = e.tokens;
|
|
45261
|
+
return {
|
|
45262
|
+
totalCostUsd: snap.totalCostUsd + e.costUsd,
|
|
45263
|
+
totalEstimatedCostUsd: snap.totalEstimatedCostUsd + e.estimatedCostUsd,
|
|
45264
|
+
totalExactCostUsd: snap.totalExactCostUsd + e.exactCostUsd,
|
|
45265
|
+
totalInputTokens: snap.totalInputTokens + (tokens?.input ?? 0),
|
|
45266
|
+
totalOutputTokens: snap.totalOutputTokens + (tokens?.output ?? 0),
|
|
45267
|
+
totalCacheReadTokens: (snap.totalCacheReadTokens ?? 0) + (tokens?.cacheRead ?? 0),
|
|
45268
|
+
totalCacheWriteTokens: (snap.totalCacheWriteTokens ?? 0) + (tokens?.cacheWrite ?? 0),
|
|
45269
|
+
tokenUsageCount: (snap.tokenUsageCount ?? 0) + (tokens ? 1 : 0),
|
|
45270
|
+
callCount: snap.callCount + 1,
|
|
45271
|
+
errorCount: snap.errorCount,
|
|
45272
|
+
totalErrorCostUsd: snap.totalErrorCostUsd
|
|
45273
|
+
};
|
|
45274
|
+
}
|
|
45275
|
+
function accumulateError(snap, e) {
|
|
45276
|
+
return {
|
|
45277
|
+
...snap,
|
|
45278
|
+
errorCount: snap.errorCount + 1,
|
|
45279
|
+
totalErrorCostUsd: snap.totalErrorCostUsd + (e?.costUsd ?? 0)
|
|
45280
|
+
};
|
|
45281
|
+
}
|
|
45282
|
+
|
|
45283
|
+
class CostAggregator {
|
|
45284
|
+
_runId;
|
|
45285
|
+
_drainDir;
|
|
45286
|
+
_events = [];
|
|
45287
|
+
_errors = [];
|
|
45288
|
+
_draining = false;
|
|
45289
|
+
_inFlightEvents = [];
|
|
45290
|
+
_inFlightErrors = [];
|
|
45291
|
+
_openScopes = new Set;
|
|
45292
|
+
constructor(_runId, _drainDir) {
|
|
45293
|
+
this._runId = _runId;
|
|
45294
|
+
this._drainDir = _drainDir;
|
|
45295
|
+
}
|
|
45296
|
+
record(event) {
|
|
45297
|
+
if (this._draining) {
|
|
45298
|
+
this._inFlightEvents.push(event);
|
|
45299
|
+
return;
|
|
45300
|
+
}
|
|
45301
|
+
this._events.push(event);
|
|
45302
|
+
}
|
|
45303
|
+
recordError(event) {
|
|
45304
|
+
if (this._draining) {
|
|
45305
|
+
this._inFlightErrors.push(event);
|
|
45306
|
+
return;
|
|
45307
|
+
}
|
|
45308
|
+
this._errors.push(event);
|
|
45309
|
+
}
|
|
45310
|
+
recordOperationSummary(_event) {}
|
|
45311
|
+
snapshot() {
|
|
45312
|
+
const allEvents = [...this._events, ...this._inFlightEvents];
|
|
45313
|
+
const allErrors = [...this._errors, ...this._inFlightErrors];
|
|
45314
|
+
const eventReduced = allEvents.reduce(accumulate, emptySnap());
|
|
45315
|
+
return allErrors.reduce((snap, e) => accumulateError(snap, e), eventReduced);
|
|
45316
|
+
}
|
|
45317
|
+
byAgent() {
|
|
45318
|
+
const m = {};
|
|
45319
|
+
for (const e of this._events)
|
|
45320
|
+
m[e.agentName] = accumulate(m[e.agentName] ?? emptySnap(), e);
|
|
45321
|
+
for (const e of this._inFlightEvents)
|
|
45322
|
+
m[e.agentName] = accumulate(m[e.agentName] ?? emptySnap(), e);
|
|
45323
|
+
for (const e of this._errors)
|
|
45324
|
+
m[e.agentName] = accumulateError(m[e.agentName] ?? emptySnap(), e);
|
|
45325
|
+
for (const e of this._inFlightErrors)
|
|
45326
|
+
m[e.agentName] = accumulateError(m[e.agentName] ?? emptySnap(), e);
|
|
45327
|
+
return m;
|
|
45328
|
+
}
|
|
45329
|
+
byStage() {
|
|
45330
|
+
const m = {};
|
|
45331
|
+
for (const e of this._events) {
|
|
45332
|
+
const k = e.stage ?? "unknown";
|
|
45333
|
+
m[k] = accumulate(m[k] ?? emptySnap(), e);
|
|
45334
|
+
}
|
|
45335
|
+
for (const e of this._inFlightEvents) {
|
|
45336
|
+
const k = e.stage ?? "unknown";
|
|
45337
|
+
m[k] = accumulate(m[k] ?? emptySnap(), e);
|
|
45338
|
+
}
|
|
45339
|
+
for (const e of this._errors) {
|
|
45340
|
+
const k = e.stage ?? "unknown";
|
|
45341
|
+
m[k] = accumulateError(m[k] ?? emptySnap(), e);
|
|
45342
|
+
}
|
|
45343
|
+
for (const e of this._inFlightErrors) {
|
|
45344
|
+
const k = e.stage ?? "unknown";
|
|
45345
|
+
m[k] = accumulateError(m[k] ?? emptySnap(), e);
|
|
45346
|
+
}
|
|
45347
|
+
return m;
|
|
45348
|
+
}
|
|
45349
|
+
byStory() {
|
|
45350
|
+
const m = {};
|
|
45351
|
+
for (const e of this._events) {
|
|
45352
|
+
const k = e.storyId ?? "unknown";
|
|
45353
|
+
m[k] = accumulate(m[k] ?? emptySnap(), e);
|
|
45354
|
+
}
|
|
45355
|
+
for (const e of this._inFlightEvents) {
|
|
45356
|
+
const k = e.storyId ?? "unknown";
|
|
45357
|
+
m[k] = accumulate(m[k] ?? emptySnap(), e);
|
|
45358
|
+
}
|
|
45359
|
+
for (const e of this._errors) {
|
|
45360
|
+
const k = e.storyId ?? "unknown";
|
|
45361
|
+
m[k] = accumulateError(m[k] ?? emptySnap(), e);
|
|
45362
|
+
}
|
|
45363
|
+
for (const e of this._inFlightErrors) {
|
|
45364
|
+
const k = e.storyId ?? "unknown";
|
|
45365
|
+
m[k] = accumulateError(m[k] ?? emptySnap(), e);
|
|
45366
|
+
}
|
|
45367
|
+
return m;
|
|
45368
|
+
}
|
|
45369
|
+
byCall() {
|
|
45370
|
+
const m = {};
|
|
45371
|
+
for (const e of [...this._events, ...this._inFlightEvents]) {
|
|
45372
|
+
if (e.callId === undefined)
|
|
45373
|
+
continue;
|
|
45374
|
+
m[e.callId] = accumulate(m[e.callId] ?? emptySnap(), e);
|
|
45375
|
+
}
|
|
45376
|
+
for (const e of [...this._errors, ...this._inFlightErrors]) {
|
|
45377
|
+
if (e.callId === undefined)
|
|
45378
|
+
continue;
|
|
45379
|
+
m[e.callId] = accumulateError(m[e.callId] ?? emptySnap(), e);
|
|
45380
|
+
}
|
|
45381
|
+
return m;
|
|
45382
|
+
}
|
|
45383
|
+
byScope() {
|
|
45384
|
+
const m = {};
|
|
45385
|
+
for (const e of [...this._events, ...this._inFlightEvents]) {
|
|
45386
|
+
if (e.scopeId === undefined)
|
|
45387
|
+
continue;
|
|
45388
|
+
m[e.scopeId] = accumulate(m[e.scopeId] ?? emptySnap(), e);
|
|
45389
|
+
}
|
|
45390
|
+
for (const e of [...this._errors, ...this._inFlightErrors]) {
|
|
45391
|
+
if (e.scopeId === undefined)
|
|
45392
|
+
continue;
|
|
45393
|
+
m[e.scopeId] = accumulateError(m[e.scopeId] ?? emptySnap(), e);
|
|
45394
|
+
}
|
|
45395
|
+
return m;
|
|
45396
|
+
}
|
|
45397
|
+
openScope(scopeId) {
|
|
45398
|
+
const id = scopeId ?? _costAggDeps.newCorrelationId();
|
|
45399
|
+
this._openScopes.add(id);
|
|
45400
|
+
let closed = false;
|
|
45401
|
+
return {
|
|
45402
|
+
scopeId: id,
|
|
45403
|
+
snapshot: () => {
|
|
45404
|
+
const matching = [...this._events, ...this._inFlightEvents].filter((e) => e.scopeId === id);
|
|
45405
|
+
const matchingErrors = [...this._errors, ...this._inFlightErrors].filter((e) => e.scopeId === id);
|
|
45406
|
+
const eventSnapshot = matching.reduce(accumulate, emptySnap());
|
|
45407
|
+
return matchingErrors.reduce((snap, e) => accumulateError(snap, e), eventSnapshot);
|
|
45408
|
+
},
|
|
45409
|
+
close: () => {
|
|
45410
|
+
if (closed)
|
|
45411
|
+
return;
|
|
45412
|
+
closed = true;
|
|
45413
|
+
this._openScopes.delete(id);
|
|
45414
|
+
}
|
|
45415
|
+
};
|
|
45416
|
+
}
|
|
45417
|
+
async drain() {
|
|
45418
|
+
const openScopeCount = this._openScopes.size;
|
|
45419
|
+
if (openScopeCount > 0) {
|
|
45420
|
+
_costAggDeps.getSafeLogger()?.warn("cost-aggregator", "drain called with open scopes", { openScopeCount });
|
|
45421
|
+
}
|
|
45422
|
+
this._draining = true;
|
|
45423
|
+
try {
|
|
45424
|
+
let committed = [...this._events.splice(0), ...this._errors.splice(0)];
|
|
45425
|
+
if (committed.length === 0 && this._inFlightEvents.length === 0 && this._inFlightErrors.length === 0) {
|
|
45426
|
+
return;
|
|
45427
|
+
}
|
|
45428
|
+
mkdirSync2(this._drainDir, { recursive: true });
|
|
45429
|
+
const path5 = join22(this._drainDir, `${this._runId}.jsonl`);
|
|
45430
|
+
let first = true;
|
|
45431
|
+
let pass = 0;
|
|
45432
|
+
while (first || this._inFlightEvents.length > 0 || this._inFlightErrors.length > 0) {
|
|
45433
|
+
if (++pass > MAX_DRAIN_PASSES) {
|
|
45434
|
+
_costAggDeps.getSafeLogger()?.warn("cost-aggregator", "drain exceeded max passes \u2014 late events keep arriving", {
|
|
45435
|
+
pass,
|
|
45436
|
+
inFlightEvents: this._inFlightEvents.length,
|
|
45437
|
+
inFlightErrors: this._inFlightErrors.length
|
|
45438
|
+
});
|
|
45439
|
+
break;
|
|
45440
|
+
}
|
|
45441
|
+
first = false;
|
|
45442
|
+
const late = [...this._inFlightEvents.splice(0), ...this._inFlightErrors.splice(0)];
|
|
45443
|
+
committed = [...committed, ...late].sort((a, b) => a.ts - b.ts);
|
|
45444
|
+
await _costAggDeps.write(path5, `${committed.map((e) => JSON.stringify(e)).join(`
|
|
45445
|
+
`)}
|
|
45446
|
+
`);
|
|
45447
|
+
}
|
|
45448
|
+
const committedEvents = committed.filter((e) => !(("kind" in e) && e.kind === "error"));
|
|
45449
|
+
const committedErrors = committed.filter((e) => ("kind" in e) && e.kind === "error");
|
|
45450
|
+
this._events.length = 0;
|
|
45451
|
+
this._events.push(...committedEvents);
|
|
45452
|
+
this._errors.length = 0;
|
|
45453
|
+
this._errors.push(...committedErrors);
|
|
45454
|
+
} finally {
|
|
45455
|
+
this._draining = false;
|
|
45456
|
+
}
|
|
45457
|
+
}
|
|
45458
|
+
}
|
|
45459
|
+
var MAX_DRAIN_PASSES = 20, EMPTY_SNAPSHOT, _costAggDeps;
|
|
45460
|
+
var init_cost_aggregator = __esm(() => {
|
|
45461
|
+
init_logger2();
|
|
45462
|
+
EMPTY_SNAPSHOT = {
|
|
45463
|
+
totalCostUsd: 0,
|
|
45464
|
+
totalEstimatedCostUsd: 0,
|
|
45465
|
+
totalExactCostUsd: 0,
|
|
45466
|
+
totalInputTokens: 0,
|
|
45467
|
+
totalOutputTokens: 0,
|
|
45468
|
+
totalCacheReadTokens: 0,
|
|
45469
|
+
totalCacheWriteTokens: 0,
|
|
45470
|
+
tokenUsageCount: 0,
|
|
45471
|
+
callCount: 0,
|
|
45472
|
+
errorCount: 0,
|
|
45473
|
+
totalErrorCostUsd: 0
|
|
45474
|
+
};
|
|
45475
|
+
_costAggDeps = {
|
|
45476
|
+
write: (path5, data) => Bun.write(path5, data),
|
|
45477
|
+
getSafeLogger: () => getSafeLogger(),
|
|
45478
|
+
newCorrelationId: makeCorrelationId
|
|
45479
|
+
};
|
|
45480
|
+
});
|
|
45481
|
+
|
|
45073
45482
|
// src/utils/file-lock.ts
|
|
45074
45483
|
import { readFile as readFile6, stat as stat4, unlink as unlink3, writeFile as writeFile5 } from "fs/promises";
|
|
45075
45484
|
function unlinkQuiet(path5) {
|
|
@@ -45382,7 +45791,7 @@ async function collectStoryMetrics(ctx, storyStartTime) {
|
|
|
45382
45791
|
attempts,
|
|
45383
45792
|
finalTier,
|
|
45384
45793
|
success: agentResult?.success || false,
|
|
45385
|
-
cost: costSnapshot
|
|
45794
|
+
cost: costSnapshot !== undefined ? totalSpendUsd(costSnapshot) : 0,
|
|
45386
45795
|
durationMs: agentResult?.durationMs || 0,
|
|
45387
45796
|
firstPassSuccess,
|
|
45388
45797
|
startedAt: storyStartTime,
|
|
@@ -45393,7 +45802,8 @@ async function collectStoryMetrics(ctx, storyStartTime) {
|
|
|
45393
45802
|
reviewsFailedOpen: ctx.reviewsFailedOpen ?? 0,
|
|
45394
45803
|
...tokens !== undefined ? { tokens, tokenAttribution: "direct" } : {},
|
|
45395
45804
|
...contextMetrics !== undefined && { context: contextMetrics },
|
|
45396
|
-
...fallbackHops.length > 0 && { fallback: { hops: fallbackHops } }
|
|
45805
|
+
...fallbackHops.length > 0 && { fallback: { hops: fallbackHops } },
|
|
45806
|
+
...costSnapshot?.totalErrorCostUsd > 0 ? { errorCostUsd: costSnapshot.totalErrorCostUsd } : {}
|
|
45397
45807
|
};
|
|
45398
45808
|
}
|
|
45399
45809
|
function collectBatchMetrics(ctx, storyStartTime) {
|
|
@@ -45401,9 +45811,8 @@ function collectBatchMetrics(ctx, storyStartTime) {
|
|
|
45401
45811
|
const routing = ctx.routing;
|
|
45402
45812
|
const agentResult = ctx.agentResult;
|
|
45403
45813
|
const batchSnapshot = ctx.runtime.costAggregator.byStory()[ctx.story.id];
|
|
45404
|
-
const
|
|
45814
|
+
const errorCostPerStory = (batchSnapshot?.totalErrorCostUsd ?? 0) / stories.length;
|
|
45405
45815
|
const totalDuration = agentResult?.durationMs || 0;
|
|
45406
|
-
const costPerStory = batchTotal / stories.length;
|
|
45407
45816
|
const durationPerStory = totalDuration / stories.length;
|
|
45408
45817
|
const tokensPerStory = tokensFromSnapshot(batchSnapshot, stories.length);
|
|
45409
45818
|
const batchAgentUsed = routing.agent ?? ctx.agentManager?.getDefault() ?? resolveDefaultAgent(ctx.config);
|
|
@@ -45425,7 +45834,8 @@ function collectBatchMetrics(ctx, storyStartTime) {
|
|
|
45425
45834
|
attempts: 1,
|
|
45426
45835
|
finalTier: routing.modelTier,
|
|
45427
45836
|
success: true,
|
|
45428
|
-
cost:
|
|
45837
|
+
cost: (batchSnapshot !== undefined ? totalSpendUsd(batchSnapshot) : 0) / stories.length,
|
|
45838
|
+
...errorCostPerStory > 0 ? { errorCostUsd: errorCostPerStory } : {},
|
|
45429
45839
|
durationMs: durationPerStory,
|
|
45430
45840
|
firstPassSuccess: true,
|
|
45431
45841
|
startedAt: storyStartTime,
|
|
@@ -45515,6 +45925,7 @@ var init_tracker = __esm(() => {
|
|
|
45515
45925
|
init_schema();
|
|
45516
45926
|
init_manifest_store();
|
|
45517
45927
|
init_logger2();
|
|
45928
|
+
init_cost_aggregator();
|
|
45518
45929
|
init_json_file();
|
|
45519
45930
|
init_path_file_lock();
|
|
45520
45931
|
});
|
|
@@ -45807,7 +46218,7 @@ var init_static_rules_budget_notice = __esm(() => {
|
|
|
45807
46218
|
|
|
45808
46219
|
// src/context/engine/providers/static-rules.ts
|
|
45809
46220
|
import { createHash as createHash11 } from "crypto";
|
|
45810
|
-
import { join as
|
|
46221
|
+
import { join as join23, relative as relative11 } from "path";
|
|
45811
46222
|
function contentHash87(content) {
|
|
45812
46223
|
return createHash11("sha256").update(content).digest("hex").slice(0, 8);
|
|
45813
46224
|
}
|
|
@@ -46119,7 +46530,7 @@ ${section.content}`;
|
|
|
46119
46530
|
const existingCandidates = [];
|
|
46120
46531
|
for (const fileName of LEGACY_CANDIDATE_FILES) {
|
|
46121
46532
|
try {
|
|
46122
|
-
if (await _staticRulesDeps.fileExists(
|
|
46533
|
+
if (await _staticRulesDeps.fileExists(join23(rootDir, fileName))) {
|
|
46123
46534
|
existingCandidates.push(fileName);
|
|
46124
46535
|
}
|
|
46125
46536
|
} catch {}
|
|
@@ -46135,11 +46546,11 @@ ${section.content}`;
|
|
|
46135
46546
|
for (const fileName of LEGACY_CANDIDATE_FILES) {
|
|
46136
46547
|
legacySources.push({
|
|
46137
46548
|
sourceId: fileName,
|
|
46138
|
-
filePath:
|
|
46549
|
+
filePath: join23(rootDir, fileName),
|
|
46139
46550
|
heading: fileName
|
|
46140
46551
|
});
|
|
46141
46552
|
}
|
|
46142
|
-
const rulesDir =
|
|
46553
|
+
const rulesDir = join23(rootDir, LEGACY_RULES_DIR);
|
|
46143
46554
|
const nestedRulePaths = _staticRulesDeps.globInDir(rulesDir);
|
|
46144
46555
|
for (const filePath of nestedRulePaths) {
|
|
46145
46556
|
const normalized = normalizePath2(filePath);
|
|
@@ -46202,7 +46613,7 @@ var init_static_rules = __esm(() => {
|
|
|
46202
46613
|
fileExists: async (path6) => Bun.file(path6).exists(),
|
|
46203
46614
|
globInDir: (dir) => {
|
|
46204
46615
|
try {
|
|
46205
|
-
return [...new Bun.Glob("**/*.md").scanSync({ cwd: dir, absolute: false })].sort().map((f) =>
|
|
46616
|
+
return [...new Bun.Glob("**/*.md").scanSync({ cwd: dir, absolute: false })].sort().map((f) => join23(dir, f));
|
|
46206
46617
|
} catch {
|
|
46207
46618
|
return [];
|
|
46208
46619
|
}
|
|
@@ -46570,7 +46981,7 @@ var init_provider_weights_cache = __esm(() => {
|
|
|
46570
46981
|
});
|
|
46571
46982
|
|
|
46572
46983
|
// src/context/engine/providers/plugin-loader.ts
|
|
46573
|
-
import { isAbsolute as isAbsolute8, join as
|
|
46984
|
+
import { isAbsolute as isAbsolute8, join as join24, resolve as resolve15 } from "path";
|
|
46574
46985
|
function isInitialisable(p) {
|
|
46575
46986
|
return typeof p.init === "function";
|
|
46576
46987
|
}
|
|
@@ -46598,7 +47009,7 @@ function resolveModuleSpecifier(specifier, workdir) {
|
|
|
46598
47009
|
}
|
|
46599
47010
|
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
46600
47011
|
const resolvedWorkdir = resolve15(workdir);
|
|
46601
|
-
const resolved = resolve15(
|
|
47012
|
+
const resolved = resolve15(join24(workdir, specifier));
|
|
46602
47013
|
if (resolved !== resolvedWorkdir && !resolved.startsWith(`${resolvedWorkdir}/`)) {
|
|
46603
47014
|
throw new Error(`Plugin module path escapes project workdir: "${specifier}" resolves to "${resolved}" (workdir: "${resolvedWorkdir}")`);
|
|
46604
47015
|
}
|
|
@@ -46740,7 +47151,7 @@ var init_plugin_cache = __esm(() => {
|
|
|
46740
47151
|
|
|
46741
47152
|
// src/context/engine/stage-assembler.ts
|
|
46742
47153
|
import { readdir as readdir2 } from "fs/promises";
|
|
46743
|
-
import { isAbsolute as isAbsolute9, join as
|
|
47154
|
+
import { isAbsolute as isAbsolute9, join as join25, resolve as resolve16 } from "path";
|
|
46744
47155
|
function dedupeScratchDirs(dirs) {
|
|
46745
47156
|
return [...new Set(dirs.filter((dir) => Boolean(dir)))];
|
|
46746
47157
|
}
|
|
@@ -46751,7 +47162,7 @@ async function discoverSessionScratchDirsOnDisk(projectDir, featureName, storyId
|
|
|
46751
47162
|
const logger = getLogger();
|
|
46752
47163
|
let sessionsRoot;
|
|
46753
47164
|
try {
|
|
46754
|
-
sessionsRoot =
|
|
47165
|
+
sessionsRoot = join25(featureDir(projectDir, featureName), "sessions");
|
|
46755
47166
|
} catch (err) {
|
|
46756
47167
|
logger.debug("context-v2", "discoverSessionScratchDirsOnDisk: invalid featureName \u2014 treating as no sessions", {
|
|
46757
47168
|
storyId,
|
|
@@ -46769,7 +47180,7 @@ async function discoverSessionScratchDirsOnDisk(projectDir, featureName, storyId
|
|
|
46769
47180
|
const cutoff = _stageAssemblerDeps.now() - ttlMs;
|
|
46770
47181
|
const found = [];
|
|
46771
47182
|
for (const entry of entries) {
|
|
46772
|
-
const descriptorPath =
|
|
47183
|
+
const descriptorPath = join25(sessionsRoot, entry, "descriptor.json");
|
|
46773
47184
|
try {
|
|
46774
47185
|
const parsed = await _stageAssemblerDeps.readDescriptor(descriptorPath);
|
|
46775
47186
|
if (!parsed || parsed.storyId !== storyId || !parsed.scratchDir)
|
|
@@ -47148,7 +47559,7 @@ var init_feature_context_filter = __esm(() => {
|
|
|
47148
47559
|
});
|
|
47149
47560
|
|
|
47150
47561
|
// src/context/feature-resolver.ts
|
|
47151
|
-
import { join as
|
|
47562
|
+
import { join as join26 } from "path";
|
|
47152
47563
|
var {Glob: Glob2 } = globalThis.Bun;
|
|
47153
47564
|
function disposeFeatureResolver(workdir) {
|
|
47154
47565
|
if (workdir !== undefined) {
|
|
@@ -47200,7 +47611,7 @@ async function buildIndex(workdir) {
|
|
|
47200
47611
|
}
|
|
47201
47612
|
async function tryResolveFromActiveFeature(story, workdir, activeFeature) {
|
|
47202
47613
|
const logger = getLogger();
|
|
47203
|
-
const prdPath =
|
|
47614
|
+
const prdPath = join26(featureDir(workdir, activeFeature), "prd.json");
|
|
47204
47615
|
try {
|
|
47205
47616
|
const raw = await _resolverDeps2.readFile(prdPath);
|
|
47206
47617
|
const prd = JSON.parse(raw);
|
|
@@ -47252,7 +47663,7 @@ var init_feature_resolver = __esm(() => {
|
|
|
47252
47663
|
|
|
47253
47664
|
// src/context/greenfield.ts
|
|
47254
47665
|
import { readdir as readdir3 } from "fs/promises";
|
|
47255
|
-
import { join as
|
|
47666
|
+
import { join as join27, relative as relative13 } from "path";
|
|
47256
47667
|
async function gitLsFiles2(workdir) {
|
|
47257
47668
|
try {
|
|
47258
47669
|
const proc = _greenfieldDeps.spawn(["git", "ls-files"], {
|
|
@@ -47280,7 +47691,7 @@ async function* walkFiles(root) {
|
|
|
47280
47691
|
for (const entry of entries) {
|
|
47281
47692
|
if (entry.isSymbolicLink())
|
|
47282
47693
|
continue;
|
|
47283
|
-
const full =
|
|
47694
|
+
const full = join27(dir, entry.name);
|
|
47284
47695
|
if (entry.isDirectory()) {
|
|
47285
47696
|
if (IGNORE_DIRS.has(entry.name))
|
|
47286
47697
|
continue;
|
|
@@ -47347,7 +47758,7 @@ var init_greenfield = __esm(() => {
|
|
|
47347
47758
|
});
|
|
47348
47759
|
|
|
47349
47760
|
// src/context/providers/feature-context.ts
|
|
47350
|
-
import { join as
|
|
47761
|
+
import { join as join28 } from "path";
|
|
47351
47762
|
function formatForInjection(content, featureId) {
|
|
47352
47763
|
return `${INJECTION_HEADER}
|
|
47353
47764
|
|
|
@@ -47364,7 +47775,7 @@ class FeatureContextProvider {
|
|
|
47364
47775
|
const featureId = await _featureContextDeps.resolveFeatureId(story, workdir, activeFeature);
|
|
47365
47776
|
if (!featureId)
|
|
47366
47777
|
return null;
|
|
47367
|
-
const contextPath =
|
|
47778
|
+
const contextPath = join28(featureDir(workdir, featureId), "context.md");
|
|
47368
47779
|
try {
|
|
47369
47780
|
const exists = await _featureContextDeps.fileExists(contextPath);
|
|
47370
47781
|
if (!exists)
|
|
@@ -47579,13 +47990,13 @@ __export(exports_loader, {
|
|
|
47579
47990
|
_promptLoaderDeps: () => _promptLoaderDeps,
|
|
47580
47991
|
loadOverride: () => loadOverride
|
|
47581
47992
|
});
|
|
47582
|
-
import { join as
|
|
47993
|
+
import { join as join29 } from "path";
|
|
47583
47994
|
async function loadOverride(role, workdir, config2) {
|
|
47584
47995
|
const overridePath = config2.prompts?.overrides?.[role];
|
|
47585
47996
|
if (!overridePath) {
|
|
47586
47997
|
return null;
|
|
47587
47998
|
}
|
|
47588
|
-
const absolutePath =
|
|
47999
|
+
const absolutePath = join29(workdir, overridePath);
|
|
47589
48000
|
if (!await _promptLoaderDeps.fileExists(absolutePath)) {
|
|
47590
48001
|
return null;
|
|
47591
48002
|
}
|
|
@@ -47884,7 +48295,7 @@ function composeSections(input) {
|
|
|
47884
48295
|
const unslotted = [input.role, input.task].filter((s) => !!s.content.trim());
|
|
47885
48296
|
return [...slotted, ...unslotted];
|
|
47886
48297
|
}
|
|
47887
|
-
function
|
|
48298
|
+
function join30(sections2) {
|
|
47888
48299
|
return sections2.map((s) => s.content).join(SECTION_SEP);
|
|
47889
48300
|
}
|
|
47890
48301
|
var init_compose2 = __esm(() => {
|
|
@@ -48186,11 +48597,11 @@ var init_generator_helpers = () => {};
|
|
|
48186
48597
|
|
|
48187
48598
|
// src/acceptance/generator.ts
|
|
48188
48599
|
import { existsSync as existsSync6 } from "fs";
|
|
48189
|
-
import { join as
|
|
48600
|
+
import { join as join31 } from "path";
|
|
48190
48601
|
function resolvePytestBin(packageDir) {
|
|
48191
48602
|
if (packageDir) {
|
|
48192
48603
|
for (const venvDir of [".venv", "venv", "env"]) {
|
|
48193
|
-
const candidate =
|
|
48604
|
+
const candidate = join31(packageDir, venvDir, "bin", "pytest");
|
|
48194
48605
|
if (existsSync6(candidate))
|
|
48195
48606
|
return candidate;
|
|
48196
48607
|
}
|
|
@@ -48450,7 +48861,6 @@ var init_acceptance_refine = __esm(() => {
|
|
|
48450
48861
|
kind: "complete",
|
|
48451
48862
|
name: "acceptance-refine",
|
|
48452
48863
|
stage: "acceptance",
|
|
48453
|
-
jsonMode: true,
|
|
48454
48864
|
config: acceptanceConfigSelector,
|
|
48455
48865
|
retry: { preset: "transient-network", maxAttempts: 2, baseDelayMs: 0 },
|
|
48456
48866
|
model: (_input, ctx) => ctx.config.acceptance.model,
|
|
@@ -49187,9 +49597,9 @@ var init_autofix_test_writer_strategy = __esm(() => {
|
|
|
49187
49597
|
});
|
|
49188
49598
|
|
|
49189
49599
|
// src/agents/exec-package-name.ts
|
|
49190
|
-
import { join as
|
|
49600
|
+
import { join as join32 } from "path";
|
|
49191
49601
|
async function resolvePackageName(root) {
|
|
49192
|
-
const pkgJson = Bun.file(
|
|
49602
|
+
const pkgJson = Bun.file(join32(root, "package.json"));
|
|
49193
49603
|
if (await pkgJson.exists()) {
|
|
49194
49604
|
try {
|
|
49195
49605
|
const parsed = await pkgJson.json();
|
|
@@ -49197,14 +49607,14 @@ async function resolvePackageName(root) {
|
|
|
49197
49607
|
return parsed.name;
|
|
49198
49608
|
} catch {}
|
|
49199
49609
|
}
|
|
49200
|
-
const cargoToml = Bun.file(
|
|
49610
|
+
const cargoToml = Bun.file(join32(root, "Cargo.toml"));
|
|
49201
49611
|
if (await cargoToml.exists()) {
|
|
49202
49612
|
const content = await cargoToml.text();
|
|
49203
49613
|
const match = content.match(/^\[package\][^[]*name\s*=\s*"([^"]+)"/ms);
|
|
49204
49614
|
if (match?.[1] !== undefined && match[1].length > 0)
|
|
49205
49615
|
return match[1];
|
|
49206
49616
|
}
|
|
49207
|
-
const pyproject = Bun.file(
|
|
49617
|
+
const pyproject = Bun.file(join32(root, "pyproject.toml"));
|
|
49208
49618
|
if (await pyproject.exists()) {
|
|
49209
49619
|
const content = await pyproject.text();
|
|
49210
49620
|
const match = content.match(/^\[project\][^[]*name\s*=\s*"([^"]+)"/ms);
|
|
@@ -49338,6 +49748,9 @@ function turnResultToAgentResult(r) {
|
|
|
49338
49748
|
function hopTier(hopKind, effectiveTier) {
|
|
49339
49749
|
return "tier" in hopKind ? hopKind.tier ?? effectiveTier : effectiveTier;
|
|
49340
49750
|
}
|
|
49751
|
+
function hopModelId(hopKind) {
|
|
49752
|
+
return "model" in hopKind ? hopKind.model : undefined;
|
|
49753
|
+
}
|
|
49341
49754
|
function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
49342
49755
|
const {
|
|
49343
49756
|
sessionManager,
|
|
@@ -49457,7 +49870,8 @@ function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
|
49457
49870
|
sessionName,
|
|
49458
49871
|
attempt: hopKind.attempt
|
|
49459
49872
|
});
|
|
49460
|
-
const
|
|
49873
|
+
const hopPin = hopModelId(hopKind);
|
|
49874
|
+
const modelDef = pinnedModelDef ?? (hopPin ? resolveModel(hopPin) : resolveModelForAgent(config2.models, agentName, hopTier(hopKind, effectiveTier), defaultAgent));
|
|
49461
49875
|
handle = await sessionManager.openSession(sessionName, {
|
|
49462
49876
|
agentName,
|
|
49463
49877
|
role: resolvedRunOptions.sessionRole ?? "implementer",
|
|
@@ -49465,7 +49879,7 @@ function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
|
49465
49879
|
pipelineStage: stage,
|
|
49466
49880
|
config: config2,
|
|
49467
49881
|
modelDef,
|
|
49468
|
-
...pinnedModelDef !== undefined ? {} : { modelTier: hopTier(hopKind, effectiveTier) },
|
|
49882
|
+
...pinnedModelDef !== undefined || hopPin ? {} : { modelTier: hopTier(hopKind, effectiveTier) },
|
|
49469
49883
|
timeoutSeconds: resolvedRunOptions.timeoutSeconds ?? config2.execution?.sessionTimeoutSeconds ?? DEFAULT_CONFIG.execution.sessionTimeoutSeconds,
|
|
49470
49884
|
featureName,
|
|
49471
49885
|
storyId: story.id,
|
|
@@ -49476,7 +49890,9 @@ function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
|
49476
49890
|
} else {
|
|
49477
49891
|
const pinned = hopKind.kind === "primary" && pinnedModelDef !== undefined;
|
|
49478
49892
|
const tier = hopTier(hopKind, effectiveTier);
|
|
49479
|
-
const
|
|
49893
|
+
const hopPin = hopModelId(hopKind);
|
|
49894
|
+
const resolveForHop = () => hopPin ? resolveModel(hopPin) : resolveModelForAgent(config2.models, agentName, tier, defaultAgent);
|
|
49895
|
+
const modelDef = hopKind.kind === "primary" ? pinnedModelDef ?? resolveForHop() : resolveForHop();
|
|
49480
49896
|
handle = await sessionManager.openSession(sessionName, {
|
|
49481
49897
|
agentName,
|
|
49482
49898
|
role: resolvedRunOptions.sessionRole ?? "implementer",
|
|
@@ -49484,7 +49900,7 @@ function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
|
49484
49900
|
pipelineStage: stage,
|
|
49485
49901
|
config: config2,
|
|
49486
49902
|
modelDef,
|
|
49487
|
-
...pinned ? {} : { modelTier: tier },
|
|
49903
|
+
...pinned || hopPin && !pinnedModelDef ? {} : { modelTier: tier },
|
|
49488
49904
|
timeoutSeconds: resolvedRunOptions.timeoutSeconds ?? config2.execution?.sessionTimeoutSeconds ?? DEFAULT_CONFIG.execution.sessionTimeoutSeconds,
|
|
49489
49905
|
featureName,
|
|
49490
49906
|
storyId: story.id,
|
|
@@ -49585,14 +50001,14 @@ var init_build_hop_callback = __esm(() => {
|
|
|
49585
50001
|
});
|
|
49586
50002
|
|
|
49587
50003
|
// src/runtime/packages.ts
|
|
49588
|
-
import { isAbsolute as isAbsolute10, join as
|
|
50004
|
+
import { isAbsolute as isAbsolute10, join as join33, relative as relative14 } from "path";
|
|
49589
50005
|
function packageWorkdir(view) {
|
|
49590
50006
|
const { packageDir, repoRoot } = view;
|
|
49591
50007
|
if (!packageDir)
|
|
49592
50008
|
return repoRoot;
|
|
49593
50009
|
if (!repoRoot || isAbsolute10(packageDir))
|
|
49594
50010
|
return packageDir;
|
|
49595
|
-
return
|
|
50011
|
+
return join33(repoRoot, packageDir);
|
|
49596
50012
|
}
|
|
49597
50013
|
function createPackageView(config2, packageDir, repoRoot, hasOverride) {
|
|
49598
50014
|
const memo = new Map;
|
|
@@ -49880,7 +50296,7 @@ async function callOp(ctx, op, input) {
|
|
|
49880
50296
|
const slicedConfig = ctx.packageView.select(selector);
|
|
49881
50297
|
const buildCtx = { packageView: ctx.packageView, config: slicedConfig };
|
|
49882
50298
|
const sections2 = composeSections(op.build(input, buildCtx));
|
|
49883
|
-
const prompt =
|
|
50299
|
+
const prompt = join30(sections2);
|
|
49884
50300
|
const timeoutMs = resolveTimeoutMs(op, input, buildCtx);
|
|
49885
50301
|
const callId = ctx.callId ?? newCorrelationId();
|
|
49886
50302
|
const abortSignal = ctx.signal ?? ctx.runtime.signal;
|
|
@@ -49899,7 +50315,6 @@ async function callOp(ctx, op, input) {
|
|
|
49899
50315
|
modelDef: resolved.modelDef,
|
|
49900
50316
|
modelDefFor: (agent, tier) => agent === dispatchAgent && tier === undefined ? resolved.modelDef : resolveModelForAgent(effectiveModels, agent, tier ?? effectiveTier, defaultAgent),
|
|
49901
50317
|
...resolved.modelTier !== undefined ? { modelTier: resolved.modelTier } : {},
|
|
49902
|
-
jsonMode: completeOp.jsonMode ?? false,
|
|
49903
50318
|
pipelineStage: op.stage,
|
|
49904
50319
|
storyId: ctx.storyId,
|
|
49905
50320
|
workdir: ctx.packageDir,
|
|
@@ -50838,7 +51253,6 @@ var init_classify_route = __esm(() => {
|
|
|
50838
51253
|
kind: "complete",
|
|
50839
51254
|
name: "classify-route",
|
|
50840
51255
|
stage: "run",
|
|
50841
|
-
jsonMode: true,
|
|
50842
51256
|
config: routingConfigSelector,
|
|
50843
51257
|
model: (_input, ctx) => ctx.config.routing.llm?.model ?? "balanced",
|
|
50844
51258
|
retry: (_input, ctx) => ({
|
|
@@ -50875,7 +51289,6 @@ ${storyBody}`, overridable: false }
|
|
|
50875
51289
|
kind: "complete",
|
|
50876
51290
|
name: "classify-route-batch",
|
|
50877
51291
|
stage: "run",
|
|
50878
|
-
jsonMode: true,
|
|
50879
51292
|
config: routingConfigSelector,
|
|
50880
51293
|
model: (_input, ctx) => ctx.config.routing.llm?.model ?? "balanced",
|
|
50881
51294
|
retry: (_input, ctx) => ({
|
|
@@ -51399,7 +51812,6 @@ var init_debate_judge = __esm(() => {
|
|
|
51399
51812
|
kind: "complete",
|
|
51400
51813
|
name: "debate-judge",
|
|
51401
51814
|
stage: "review",
|
|
51402
|
-
jsonMode: false,
|
|
51403
51815
|
config: debateConfigSelector,
|
|
51404
51816
|
model: (input) => ({ agent: input.resolverAgent, model: input.resolverModel }),
|
|
51405
51817
|
timeoutMs: (input) => (input.timeoutSeconds ?? 600) * 1000,
|
|
@@ -51485,7 +51897,6 @@ var init_debate_propose = __esm(() => {
|
|
|
51485
51897
|
kind: "complete",
|
|
51486
51898
|
name: "debate-propose",
|
|
51487
51899
|
stage: "review",
|
|
51488
|
-
jsonMode: false,
|
|
51489
51900
|
config: debateConfigSelector,
|
|
51490
51901
|
model: (input) => {
|
|
51491
51902
|
const debater = input.debaters[input.debaterIndex];
|
|
@@ -51513,7 +51924,6 @@ var init_debate_rebut = __esm(() => {
|
|
|
51513
51924
|
kind: "complete",
|
|
51514
51925
|
name: "debate-rebut",
|
|
51515
51926
|
stage: "review",
|
|
51516
|
-
jsonMode: false,
|
|
51517
51927
|
config: debateConfigSelector,
|
|
51518
51928
|
model: (input) => {
|
|
51519
51929
|
const debater = input.debaters[input.debaterIndex];
|
|
@@ -51578,7 +51988,6 @@ var init_debate_synthesis = __esm(() => {
|
|
|
51578
51988
|
kind: "complete",
|
|
51579
51989
|
name: "debate-synthesis",
|
|
51580
51990
|
stage: "review",
|
|
51581
|
-
jsonMode: false,
|
|
51582
51991
|
config: debateConfigSelector,
|
|
51583
51992
|
model: (input) => ({ agent: input.resolverAgent, model: input.resolverModel }),
|
|
51584
51993
|
timeoutMs: (input) => (input.timeoutSeconds ?? 600) * 1000,
|
|
@@ -51686,7 +52095,6 @@ var init_decompose2 = __esm(() => {
|
|
|
51686
52095
|
kind: "complete",
|
|
51687
52096
|
name: "decompose",
|
|
51688
52097
|
stage: "plan",
|
|
51689
|
-
jsonMode: false,
|
|
51690
52098
|
config: decomposeConfigSelector,
|
|
51691
52099
|
model: (_input, ctx) => ctx.config.plan.model,
|
|
51692
52100
|
timeoutMs: (_input, ctx) => (ctx.config.plan.decomposeTimeoutSeconds ?? ctx.config.plan.timeoutSeconds ?? 600) * 1000,
|
|
@@ -52886,7 +53294,7 @@ __export(exports_command_resolver, {
|
|
|
52886
53294
|
_commandResolverDeps: () => _commandResolverDeps,
|
|
52887
53295
|
resolveQualityTestCommands: () => resolveQualityTestCommands
|
|
52888
53296
|
});
|
|
52889
|
-
import { join as
|
|
53297
|
+
import { join as join34 } from "path";
|
|
52890
53298
|
async function resolveQualityTestCommands(config2, workdir, storyWorkdir) {
|
|
52891
53299
|
const rawTestCommand = config2.review?.commands?.test ?? config2.quality?.commands?.test;
|
|
52892
53300
|
const rawScopedTemplate = config2.quality?.commands?.testScoped;
|
|
@@ -52913,7 +53321,7 @@ var init_command_resolver = __esm(() => {
|
|
|
52913
53321
|
_commandResolverDeps = {
|
|
52914
53322
|
readPackageName: async (dir) => {
|
|
52915
53323
|
try {
|
|
52916
|
-
const content = await Bun.file(
|
|
53324
|
+
const content = await Bun.file(join34(dir, "package.json")).json();
|
|
52917
53325
|
return typeof content.name === "string" ? content.name : null;
|
|
52918
53326
|
} catch {
|
|
52919
53327
|
return null;
|
|
@@ -52929,7 +53337,7 @@ __export(exports_command_defaults, {
|
|
|
52929
53337
|
clearCommandDefaultsCache: () => clearCommandDefaultsCache,
|
|
52930
53338
|
resolveDefaultQualityCommands: () => resolveDefaultQualityCommands
|
|
52931
53339
|
});
|
|
52932
|
-
import { join as
|
|
53340
|
+
import { join as join35 } from "path";
|
|
52933
53341
|
function clearCommandDefaultsCache() {
|
|
52934
53342
|
memo.clear();
|
|
52935
53343
|
}
|
|
@@ -52961,18 +53369,18 @@ async function compute(packageDir) {
|
|
|
52961
53369
|
}
|
|
52962
53370
|
async function pythonDefaults(packageDir) {
|
|
52963
53371
|
const deps = _commandDefaultsDeps;
|
|
52964
|
-
const pyproject = await deps.readText(
|
|
53372
|
+
const pyproject = await deps.readText(join35(packageDir, "pyproject.toml"));
|
|
52965
53373
|
let prefix = "";
|
|
52966
|
-
if (await deps.fileExists(
|
|
53374
|
+
if (await deps.fileExists(join35(packageDir, "uv.lock")) || pyproject.includes("[tool.uv]")) {
|
|
52967
53375
|
prefix = "uv run ";
|
|
52968
|
-
} else if (await deps.fileExists(
|
|
53376
|
+
} else if (await deps.fileExists(join35(packageDir, "poetry.lock")) || pyproject.includes("[tool.poetry]")) {
|
|
52969
53377
|
prefix = "poetry run ";
|
|
52970
53378
|
}
|
|
52971
53379
|
const result = { test: `${prefix}pytest` };
|
|
52972
|
-
if (pyproject.includes("[tool.mypy]") || await deps.fileExists(
|
|
53380
|
+
if (pyproject.includes("[tool.mypy]") || await deps.fileExists(join35(packageDir, "mypy.ini"))) {
|
|
52973
53381
|
result.typecheck = `${prefix}mypy .`;
|
|
52974
53382
|
}
|
|
52975
|
-
if (pyproject.includes("[tool.ruff]") || await deps.fileExists(
|
|
53383
|
+
if (pyproject.includes("[tool.ruff]") || await deps.fileExists(join35(packageDir, "ruff.toml")) || await deps.fileExists(join35(packageDir, ".ruff.toml"))) {
|
|
52976
53384
|
result.lint = `${prefix}ruff check .`;
|
|
52977
53385
|
}
|
|
52978
53386
|
return result;
|
|
@@ -52980,7 +53388,7 @@ async function pythonDefaults(packageDir) {
|
|
|
52980
53388
|
async function jsDefaults(packageDir) {
|
|
52981
53389
|
const deps = _commandDefaultsDeps;
|
|
52982
53390
|
const pm = await detectJsPackageManager(packageDir);
|
|
52983
|
-
const pkg = await deps.readJson(
|
|
53391
|
+
const pkg = await deps.readJson(join35(packageDir, "package.json"));
|
|
52984
53392
|
const scripts = pkg?.scripts ?? {};
|
|
52985
53393
|
const result = {};
|
|
52986
53394
|
if (typeof scripts.test === "string" && scripts.test.trim().length > 0) {
|
|
@@ -52990,24 +53398,24 @@ async function jsDefaults(packageDir) {
|
|
|
52990
53398
|
}
|
|
52991
53399
|
if (typeof scripts.typecheck === "string") {
|
|
52992
53400
|
result.typecheck = `${pm} run typecheck`;
|
|
52993
|
-
} else if (await deps.fileExists(
|
|
53401
|
+
} else if (await deps.fileExists(join35(packageDir, "tsconfig.json"))) {
|
|
52994
53402
|
result.typecheck = `${pm === "bun" ? "bunx" : `${pm} exec`} tsc --noEmit`;
|
|
52995
53403
|
}
|
|
52996
|
-
if (await deps.fileExists(
|
|
53404
|
+
if (await deps.fileExists(join35(packageDir, "biome.json"))) {
|
|
52997
53405
|
result.lint = `${pm === "bun" ? "bunx" : `${pm} exec`} biome check .`;
|
|
52998
|
-
} else if (await deps.fileExists(
|
|
53406
|
+
} else if (await deps.fileExists(join35(packageDir, ".eslintrc")) || await deps.fileExists(join35(packageDir, ".eslintrc.json")) || await deps.fileExists(join35(packageDir, ".eslintrc.js")) || await deps.fileExists(join35(packageDir, "eslint.config.js"))) {
|
|
52999
53407
|
result.lint = `${pm === "bun" ? "bunx" : `${pm} exec`} eslint .`;
|
|
53000
53408
|
}
|
|
53001
53409
|
return result;
|
|
53002
53410
|
}
|
|
53003
53411
|
async function detectJsPackageManager(packageDir) {
|
|
53004
53412
|
const deps = _commandDefaultsDeps;
|
|
53005
|
-
if (await deps.fileExists(
|
|
53413
|
+
if (await deps.fileExists(join35(packageDir, "bun.lock")) || await deps.fileExists(join35(packageDir, "bun.lockb"))) {
|
|
53006
53414
|
return "bun";
|
|
53007
53415
|
}
|
|
53008
|
-
if (await deps.fileExists(
|
|
53416
|
+
if (await deps.fileExists(join35(packageDir, "pnpm-lock.yaml")))
|
|
53009
53417
|
return "pnpm";
|
|
53010
|
-
if (await deps.fileExists(
|
|
53418
|
+
if (await deps.fileExists(join35(packageDir, "yarn.lock")))
|
|
53011
53419
|
return "yarn";
|
|
53012
53420
|
return "npm";
|
|
53013
53421
|
}
|
|
@@ -53045,13 +53453,13 @@ __export(exports_runners, {
|
|
|
53045
53453
|
verifyAssets: () => verifyAssets
|
|
53046
53454
|
});
|
|
53047
53455
|
import { existsSync as existsSync7 } from "fs";
|
|
53048
|
-
import { join as
|
|
53456
|
+
import { join as join36 } from "path";
|
|
53049
53457
|
async function verifyAssets(workingDirectory, expectedFiles) {
|
|
53050
53458
|
if (!expectedFiles || expectedFiles.length === 0)
|
|
53051
53459
|
return { success: true, missingFiles: [] };
|
|
53052
53460
|
const missingFiles = [];
|
|
53053
53461
|
for (const file2 of expectedFiles) {
|
|
53054
|
-
if (!existsSync7(
|
|
53462
|
+
if (!existsSync7(join36(workingDirectory, file2)))
|
|
53055
53463
|
missingFiles.push(file2);
|
|
53056
53464
|
}
|
|
53057
53465
|
if (missingFiles.length > 0) {
|
|
@@ -54972,16 +55380,16 @@ var init_classify2 = __esm(() => {
|
|
|
54972
55380
|
});
|
|
54973
55381
|
|
|
54974
55382
|
// src/verification/mutation/journal.ts
|
|
54975
|
-
import { join as
|
|
55383
|
+
import { join as join37 } from "path";
|
|
54976
55384
|
function journalDir(repoRoot) {
|
|
54977
|
-
return
|
|
55385
|
+
return join37(repoRoot, JOURNAL_DIRNAME);
|
|
54978
55386
|
}
|
|
54979
55387
|
function journalFileName(storyId) {
|
|
54980
55388
|
const safe = storyId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
54981
55389
|
return `${safe || "unknown"}.json`;
|
|
54982
55390
|
}
|
|
54983
55391
|
function journalPathFor(repoRoot, storyId) {
|
|
54984
|
-
return
|
|
55392
|
+
return join37(journalDir(repoRoot), journalFileName(storyId));
|
|
54985
55393
|
}
|
|
54986
55394
|
async function mayHaveJournal(candidateRoots) {
|
|
54987
55395
|
const { stat: stat6 } = await import("fs/promises");
|
|
@@ -55028,7 +55436,7 @@ async function restoreInFlight(repoRoot) {
|
|
|
55028
55436
|
for (const name of names) {
|
|
55029
55437
|
if (!name.endsWith(".json"))
|
|
55030
55438
|
continue;
|
|
55031
|
-
const path8 =
|
|
55439
|
+
const path8 = join37(dir, name);
|
|
55032
55440
|
const entry = await readEntry(path8);
|
|
55033
55441
|
if (!entry) {
|
|
55034
55442
|
await unlink4(path8).catch(() => {});
|
|
@@ -55058,7 +55466,7 @@ var JOURNAL_DIRNAME;
|
|
|
55058
55466
|
var init_journal = __esm(() => {
|
|
55059
55467
|
init_realpath();
|
|
55060
55468
|
init_apply();
|
|
55061
|
-
JOURNAL_DIRNAME =
|
|
55469
|
+
JOURNAL_DIRNAME = join37(".nax", "mutation-journal");
|
|
55062
55470
|
});
|
|
55063
55471
|
|
|
55064
55472
|
// src/verification/mutation/operators.ts
|
|
@@ -55281,7 +55689,7 @@ var init_mutation = __esm(() => {
|
|
|
55281
55689
|
});
|
|
55282
55690
|
|
|
55283
55691
|
// src/operations/mutation-check.ts
|
|
55284
|
-
import { isAbsolute as isAbsolute11, join as
|
|
55692
|
+
import { isAbsolute as isAbsolute11, join as join38 } from "path";
|
|
55285
55693
|
async function sweepLeftoverMutants(repoRoot, storyId) {
|
|
55286
55694
|
const logger = getLogger();
|
|
55287
55695
|
try {
|
|
@@ -55391,7 +55799,7 @@ var init_mutation_check = __esm(() => {
|
|
|
55391
55799
|
const anchor = input.packagePrefix && input.repoRoot ? input.repoRoot : await deps.getGitRoot(input.workdir) ?? input.workdir;
|
|
55392
55800
|
const scopeRoot = input.repoRoot ?? input.workdir;
|
|
55393
55801
|
const absoluteChangedFiles = changedFiles2.map((f) => {
|
|
55394
|
-
const path8 = isAbsolute11(f) ? f :
|
|
55802
|
+
const path8 = isAbsolute11(f) ? f : join38(anchor, f);
|
|
55395
55803
|
return { path: path8, resolved: realOrRaw(path8) };
|
|
55396
55804
|
}).filter(({ resolved }) => isInside(scopeRoot, resolved));
|
|
55397
55805
|
const rawRangeMap = await deps.getChangedLineRanges(input.workdir, input.storyGitRef);
|
|
@@ -55898,7 +56306,7 @@ var init_plan_draft = __esm(() => {
|
|
|
55898
56306
|
});
|
|
55899
56307
|
|
|
55900
56308
|
// src/operations/plan-refine.ts
|
|
55901
|
-
import { join as
|
|
56309
|
+
import { join as join39 } from "path";
|
|
55902
56310
|
function hasToken(text, tokens) {
|
|
55903
56311
|
const lower = text.toLowerCase();
|
|
55904
56312
|
return tokens.some((token) => lower.includes(token));
|
|
@@ -55976,7 +56384,7 @@ async function normalizeStoryFiles(story, workdir, fileExists, upstreamProduced)
|
|
|
55976
56384
|
for (const entry of contextFiles) {
|
|
55977
56385
|
const filePath = typeof entry === "string" ? entry : entry.path;
|
|
55978
56386
|
const factId = typeof entry === "string" ? undefined : entry.factId;
|
|
55979
|
-
if (expected.has(filePath) || await fileExists(
|
|
56387
|
+
if (expected.has(filePath) || await fileExists(join39(workdir, filePath))) {
|
|
55980
56388
|
kept.push(entry);
|
|
55981
56389
|
continue;
|
|
55982
56390
|
}
|
|
@@ -56739,14 +57147,14 @@ var init_typecheck_check = __esm(() => {
|
|
|
56739
57147
|
});
|
|
56740
57148
|
|
|
56741
57149
|
// src/operations/validate-mock-structure-files.ts
|
|
56742
|
-
import { isAbsolute as isAbsolute12, join as
|
|
57150
|
+
import { isAbsolute as isAbsolute12, join as join40, relative as relative15 } from "path";
|
|
56743
57151
|
function resolutionCandidates(file2, packageDir, repoRoot) {
|
|
56744
57152
|
if (isAbsolute12(file2))
|
|
56745
57153
|
return [file2];
|
|
56746
|
-
const viaPackageDir =
|
|
57154
|
+
const viaPackageDir = join40(packageDir, file2);
|
|
56747
57155
|
if (repoRoot === undefined)
|
|
56748
57156
|
return [viaPackageDir];
|
|
56749
|
-
const viaRepoRoot =
|
|
57157
|
+
const viaRepoRoot = join40(repoRoot, file2);
|
|
56750
57158
|
return viaRepoRoot === viaPackageDir ? [viaPackageDir] : [viaPackageDir, viaRepoRoot];
|
|
56751
57159
|
}
|
|
56752
57160
|
async function resolvePackageRelative(file2, opts) {
|
|
@@ -57661,20 +58069,67 @@ var init_operations = __esm(() => {
|
|
|
57661
58069
|
});
|
|
57662
58070
|
|
|
57663
58071
|
// src/findings/cycle-cost.ts
|
|
57664
|
-
function
|
|
58072
|
+
function ledgerSpendFor(ctx, callId) {
|
|
57665
58073
|
try {
|
|
57666
|
-
|
|
58074
|
+
const snap = ctx.runtime.costAggregator.byCall()[callId];
|
|
58075
|
+
if (snap === undefined)
|
|
58076
|
+
return { costUsd: 0, errorCostUsd: 0 };
|
|
58077
|
+
return { costUsd: totalSpendUsd(snap), errorCostUsd: snap.totalErrorCostUsd };
|
|
57667
58078
|
} catch (err) {
|
|
57668
58079
|
getSafeLogger()?.debug("findings.cycle", "cost ledger read failed; reporting 0 for this dispatch", {
|
|
57669
58080
|
storyId: ctx.storyId,
|
|
57670
58081
|
callId,
|
|
57671
58082
|
error: errorMessage(err)
|
|
57672
58083
|
});
|
|
57673
|
-
return 0;
|
|
58084
|
+
return { costUsd: 0, errorCostUsd: 0 };
|
|
57674
58085
|
}
|
|
57675
58086
|
}
|
|
57676
58087
|
var init_cycle_cost = __esm(() => {
|
|
57677
58088
|
init_logger2();
|
|
58089
|
+
init_cost_aggregator();
|
|
58090
|
+
});
|
|
58091
|
+
|
|
58092
|
+
// src/findings/cycle-dispatch.ts
|
|
58093
|
+
async function dispatchStrategy(strategy, ctx, findingsBefore, priorIterations, deps) {
|
|
58094
|
+
const { dispatchCallId } = deps;
|
|
58095
|
+
const relevantFindings = findingsBefore.filter((f) => strategy.appliesTo(f));
|
|
58096
|
+
const input = strategy.buildInput(relevantFindings, priorIterations, ctx);
|
|
58097
|
+
const fixCtx = {
|
|
58098
|
+
...ctx,
|
|
58099
|
+
callId: dispatchCallId,
|
|
58100
|
+
fixStrategy: { name: strategy.name, findingsBefore: findingsBefore.length },
|
|
58101
|
+
...strategy.sessionRole ? { sessionOverride: { role: strategy.sessionRole } } : {}
|
|
58102
|
+
};
|
|
58103
|
+
let output;
|
|
58104
|
+
try {
|
|
58105
|
+
output = await deps.callOp(fixCtx, strategy.fixOp, input);
|
|
58106
|
+
} catch (err) {
|
|
58107
|
+
const spend2 = ledgerSpendFor(fixCtx, dispatchCallId);
|
|
58108
|
+
deps.logger?.warn("findings.cycle", "dispatch threw \u2014 spend recorded here, not on an iteration", {
|
|
58109
|
+
...deps.logCtx,
|
|
58110
|
+
strategyName: strategy.name,
|
|
58111
|
+
op: strategy.fixOp.name,
|
|
58112
|
+
callId: dispatchCallId,
|
|
58113
|
+
costUsd: spend2.costUsd,
|
|
58114
|
+
errorCostUsd: spend2.errorCostUsd,
|
|
58115
|
+
error: errorMessage(err)
|
|
58116
|
+
});
|
|
58117
|
+
throw err;
|
|
58118
|
+
}
|
|
58119
|
+
const extracted = await (strategy.extractApplied?.(output, input) ?? {});
|
|
58120
|
+
const spend = ledgerSpendFor(fixCtx, dispatchCallId);
|
|
58121
|
+
return {
|
|
58122
|
+
strategyName: strategy.name,
|
|
58123
|
+
op: strategy.fixOp.name,
|
|
58124
|
+
targetFiles: extracted.targetFiles ?? [],
|
|
58125
|
+
summary: extracted.summary ?? "",
|
|
58126
|
+
...extracted.unresolved ? { unresolved: extracted.unresolved } : {},
|
|
58127
|
+
costUsd: extracted.costUsd !== undefined ? extracted.costUsd + spend.errorCostUsd : spend.costUsd,
|
|
58128
|
+
...spend.errorCostUsd > 0 ? { errorCostUsd: spend.errorCostUsd } : {}
|
|
58129
|
+
};
|
|
58130
|
+
}
|
|
58131
|
+
var init_cycle_dispatch = __esm(() => {
|
|
58132
|
+
init_cycle_cost();
|
|
57678
58133
|
});
|
|
57679
58134
|
|
|
57680
58135
|
// src/findings/types.ts
|
|
@@ -57708,6 +58163,7 @@ function recordIteration(cycle, input, ctx, logger) {
|
|
|
57708
58163
|
const findingRecurrenceKeysBefore = input.findingsBefore.map(findingRecurrenceKey);
|
|
57709
58164
|
const findingRecurrenceKeysAfter = input.findingsAfter.map(findingRecurrenceKey);
|
|
57710
58165
|
const costUsd = input.fixesApplied.reduce((sum, fa) => sum + (fa.costUsd ?? 0), 0);
|
|
58166
|
+
const errorCostUsd = input.fixesApplied.reduce((sum, fa) => sum + (fa.errorCostUsd ?? 0), 0);
|
|
57711
58167
|
const seenTargetFiles = new Set;
|
|
57712
58168
|
const fixTargetFiles = [];
|
|
57713
58169
|
for (const fa of input.fixesApplied) {
|
|
@@ -57733,7 +58189,8 @@ function recordIteration(cycle, input, ctx, logger) {
|
|
|
57733
58189
|
findingRecurrenceKeysBefore,
|
|
57734
58190
|
findingRecurrenceKeysAfter,
|
|
57735
58191
|
...hasFixes ? { fixTargetFiles, fixSummaries } : {},
|
|
57736
|
-
...costUsd > 0 ? { costUsd } : {}
|
|
58192
|
+
...costUsd > 0 ? { costUsd } : {},
|
|
58193
|
+
...errorCostUsd > 0 ? { errorCostUsd } : {}
|
|
57737
58194
|
};
|
|
57738
58195
|
cycle.iterations.push(iteration);
|
|
57739
58196
|
logger?.info("findings.cycle", "iteration completed", {
|
|
@@ -57750,7 +58207,8 @@ function recordIteration(cycle, input, ctx, logger) {
|
|
|
57750
58207
|
findingRecurrenceKeysBefore,
|
|
57751
58208
|
findingRecurrenceKeysAfter,
|
|
57752
58209
|
...hasFixes ? { fixTargetFiles, fixSummaries } : {},
|
|
57753
|
-
...costUsd > 0 ? { costUsd } : {}
|
|
58210
|
+
...costUsd > 0 ? { costUsd } : {},
|
|
58211
|
+
...errorCostUsd > 0 ? { errorCostUsd } : {}
|
|
57754
58212
|
});
|
|
57755
58213
|
return iteration;
|
|
57756
58214
|
}
|
|
@@ -57942,25 +58400,12 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
|
|
|
57942
58400
|
const findingsBefore = [...cycle.findings];
|
|
57943
58401
|
const fixesApplied = [];
|
|
57944
58402
|
for (const strategy of group) {
|
|
57945
|
-
|
|
57946
|
-
|
|
57947
|
-
|
|
57948
|
-
|
|
57949
|
-
|
|
57950
|
-
|
|
57951
|
-
fixStrategy: { name: strategy.name, findingsBefore: findingsBefore.length },
|
|
57952
|
-
...strategy.sessionRole ? { sessionOverride: { role: strategy.sessionRole } } : {}
|
|
57953
|
-
};
|
|
57954
|
-
const output = await doCallOp(fixCtx, strategy.fixOp, input);
|
|
57955
|
-
const extracted = await (strategy.extractApplied?.(output, input) ?? {});
|
|
57956
|
-
fixesApplied.push({
|
|
57957
|
-
strategyName: strategy.name,
|
|
57958
|
-
op: strategy.fixOp.name,
|
|
57959
|
-
targetFiles: extracted.targetFiles ?? [],
|
|
57960
|
-
summary: extracted.summary ?? "",
|
|
57961
|
-
...extracted.unresolved ? { unresolved: extracted.unresolved } : {},
|
|
57962
|
-
costUsd: extracted.costUsd ?? ledgerCostFor(fixCtx, dispatchCallId)
|
|
57963
|
-
});
|
|
58403
|
+
fixesApplied.push(await dispatchStrategy(strategy, ctx, findingsBefore, cycle.iterations, {
|
|
58404
|
+
callOp: doCallOp,
|
|
58405
|
+
dispatchCallId: newCorrelationId(),
|
|
58406
|
+
logger,
|
|
58407
|
+
logCtx
|
|
58408
|
+
}));
|
|
57964
58409
|
}
|
|
57965
58410
|
const unresolvedFas = fixesApplied.filter((fa) => fa.unresolved);
|
|
57966
58411
|
if (unresolvedFas.length > 0) {
|
|
@@ -58174,7 +58619,7 @@ var _cycleDeps;
|
|
|
58174
58619
|
var init_cycle = __esm(() => {
|
|
58175
58620
|
init_logger2();
|
|
58176
58621
|
init_operations();
|
|
58177
|
-
|
|
58622
|
+
init_cycle_dispatch();
|
|
58178
58623
|
init_cycle_iteration_log();
|
|
58179
58624
|
init_cycle_retirement();
|
|
58180
58625
|
init_types8();
|
|
@@ -59161,7 +59606,7 @@ var init_config_descriptions = __esm(() => {
|
|
|
59161
59606
|
|
|
59162
59607
|
// src/cli/config-get.ts
|
|
59163
59608
|
import { existsSync as existsSync8 } from "fs";
|
|
59164
|
-
import { join as
|
|
59609
|
+
import { join as join41 } from "path";
|
|
59165
59610
|
async function loadConfigFile(path9) {
|
|
59166
59611
|
if (!existsSync8(path9))
|
|
59167
59612
|
return null;
|
|
@@ -59183,7 +59628,7 @@ async function loadProjectConfig() {
|
|
|
59183
59628
|
const projectDir = findProjectDir();
|
|
59184
59629
|
if (!projectDir)
|
|
59185
59630
|
return null;
|
|
59186
|
-
const projectPath =
|
|
59631
|
+
const projectPath = join41(projectDir, "config.json");
|
|
59187
59632
|
return await loadConfigFile(projectPath);
|
|
59188
59633
|
}
|
|
59189
59634
|
var init_config_get = __esm(() => {
|
|
@@ -59193,11 +59638,11 @@ var init_config_get = __esm(() => {
|
|
|
59193
59638
|
});
|
|
59194
59639
|
|
|
59195
59640
|
// src/cli/config-profile.ts
|
|
59196
|
-
import { mkdirSync as
|
|
59197
|
-
import { join as
|
|
59641
|
+
import { mkdirSync as mkdirSync3, readdirSync as readdirSync2 } from "fs";
|
|
59642
|
+
import { join as join42 } from "path";
|
|
59198
59643
|
async function profileListCommand(startDir) {
|
|
59199
|
-
const globalProfilesDir =
|
|
59200
|
-
const projectProfilesDir =
|
|
59644
|
+
const globalProfilesDir = join42(globalConfigDir(), "profiles");
|
|
59645
|
+
const projectProfilesDir = join42(projectConfigDir(startDir), "profiles");
|
|
59201
59646
|
const globalProfiles = scanProfileDir(globalProfilesDir);
|
|
59202
59647
|
const projectProfiles = scanProfileDir(projectProfilesDir);
|
|
59203
59648
|
const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
@@ -59260,7 +59705,7 @@ function maskProfileValue(value) {
|
|
|
59260
59705
|
return value;
|
|
59261
59706
|
}
|
|
59262
59707
|
async function profileUseCommand(profileName, startDir) {
|
|
59263
|
-
const configPath =
|
|
59708
|
+
const configPath = join42(projectConfigDir(startDir), "config.json");
|
|
59264
59709
|
const configFile = Bun.file(configPath);
|
|
59265
59710
|
let existing = {};
|
|
59266
59711
|
if (await configFile.exists()) {
|
|
@@ -59281,13 +59726,13 @@ async function profileCurrentCommand(startDir) {
|
|
|
59281
59726
|
}
|
|
59282
59727
|
async function profileCreateCommand(profileName, startDir) {
|
|
59283
59728
|
validateProfileName(profileName);
|
|
59284
|
-
const profilesDir =
|
|
59285
|
-
const profilePath =
|
|
59729
|
+
const profilesDir = join42(projectConfigDir(startDir), "profiles");
|
|
59730
|
+
const profilePath = join42(profilesDir, `${profileName}.json`);
|
|
59286
59731
|
const profileFile = Bun.file(profilePath);
|
|
59287
59732
|
if (await profileFile.exists()) {
|
|
59288
59733
|
throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
|
|
59289
59734
|
}
|
|
59290
|
-
|
|
59735
|
+
mkdirSync3(profilesDir, { recursive: true });
|
|
59291
59736
|
await Bun.write(profilePath, "{}");
|
|
59292
59737
|
return profilePath;
|
|
59293
59738
|
}
|
|
@@ -59305,7 +59750,7 @@ var init_config_profile = __esm(() => {
|
|
|
59305
59750
|
|
|
59306
59751
|
// src/cli/config-display.ts
|
|
59307
59752
|
import { existsSync as existsSync9 } from "fs";
|
|
59308
|
-
import { join as
|
|
59753
|
+
import { join as join43 } from "path";
|
|
59309
59754
|
async function configCommand(config2, options = {}) {
|
|
59310
59755
|
const { explain = false, diff = false } = options;
|
|
59311
59756
|
if (explain && diff) {
|
|
@@ -59363,7 +59808,7 @@ async function configCommand(config2, options = {}) {
|
|
|
59363
59808
|
function determineConfigSources() {
|
|
59364
59809
|
const globalPath = globalConfigPath();
|
|
59365
59810
|
const projectDir = findProjectDir();
|
|
59366
|
-
const projectPath = projectDir ?
|
|
59811
|
+
const projectPath = projectDir ? join43(projectDir, "config.json") : null;
|
|
59367
59812
|
return {
|
|
59368
59813
|
global: fileExists(globalPath) ? globalPath : null,
|
|
59369
59814
|
project: projectPath && fileExists(projectPath) ? projectPath : null
|
|
@@ -59918,7 +60363,7 @@ var init_context2 = __esm(() => {
|
|
|
59918
60363
|
|
|
59919
60364
|
// src/cli/context-fragments.ts
|
|
59920
60365
|
import { existsSync as existsSync11 } from "fs";
|
|
59921
|
-
import { join as
|
|
60366
|
+
import { join as join44 } from "path";
|
|
59922
60367
|
function listDependentStoryIds(prd, startId) {
|
|
59923
60368
|
const forward = new Map;
|
|
59924
60369
|
for (const story of prd.userStories) {
|
|
@@ -60003,7 +60448,7 @@ function resolveProjectDir(dir) {
|
|
|
60003
60448
|
return dir ?? process.cwd();
|
|
60004
60449
|
}
|
|
60005
60450
|
function featurePrdPath2(projectDir, featureId) {
|
|
60006
|
-
return
|
|
60451
|
+
return join44(featureDir(projectDir, featureId), "prd.json");
|
|
60007
60452
|
}
|
|
60008
60453
|
async function fragmentsInspectCommand(options) {
|
|
60009
60454
|
const projectDir = _contextFragmentsDeps.projectDirFor(resolveProjectDir(options.dir));
|
|
@@ -60621,7 +61066,7 @@ var init_acceptance2 = __esm(() => {
|
|
|
60621
61066
|
|
|
60622
61067
|
// src/cli/features-acceptance.ts
|
|
60623
61068
|
import { existsSync as existsSync12 } from "fs";
|
|
60624
|
-
import { join as
|
|
61069
|
+
import { join as join45, relative as relative16 } from "path";
|
|
60625
61070
|
async function resolveFeatureAcceptance(featureName, workdir) {
|
|
60626
61071
|
let enabled = true;
|
|
60627
61072
|
try {
|
|
@@ -60629,13 +61074,13 @@ async function resolveFeatureAcceptance(featureName, workdir) {
|
|
|
60629
61074
|
if (!naxDir) {
|
|
60630
61075
|
return { status: "no-prd", enabled, groups: [] };
|
|
60631
61076
|
}
|
|
60632
|
-
const repoRoot =
|
|
61077
|
+
const repoRoot = join45(naxDir, "..");
|
|
60633
61078
|
const config2 = await loadConfig(workdir);
|
|
60634
61079
|
enabled = config2.acceptance?.enabled ?? true;
|
|
60635
61080
|
if (!enabled) {
|
|
60636
61081
|
return { status: "disabled", enabled: false, groups: [] };
|
|
60637
61082
|
}
|
|
60638
|
-
const prdPath =
|
|
61083
|
+
const prdPath = join45(naxDir, "features", featureName, "prd.json");
|
|
60639
61084
|
if (!existsSync12(prdPath)) {
|
|
60640
61085
|
return { status: "no-prd", enabled, groups: [] };
|
|
60641
61086
|
}
|
|
@@ -60694,7 +61139,7 @@ function validateFeatureName(feature) {
|
|
|
60694
61139
|
|
|
60695
61140
|
// src/cli/features-resolve.ts
|
|
60696
61141
|
import { existsSync as existsSync13, readdirSync as readdirSync3 } from "fs";
|
|
60697
|
-
import { join as
|
|
61142
|
+
import { join as join46, relative as relative17 } from "path";
|
|
60698
61143
|
async function resolveTestPatterns(workdir) {
|
|
60699
61144
|
try {
|
|
60700
61145
|
const config2 = await loadConfig(workdir);
|
|
@@ -60712,10 +61157,10 @@ async function isNonEmptyFile(absolutePath) {
|
|
|
60712
61157
|
}
|
|
60713
61158
|
async function searchSpecSource(naxDir, repoRoot, name) {
|
|
60714
61159
|
const candidates = [
|
|
60715
|
-
{ abs:
|
|
60716
|
-
{ abs:
|
|
61160
|
+
{ abs: join46(naxDir, "features", name, "spec.md"), kind: "markdown" },
|
|
61161
|
+
{ abs: join46(naxDir, "specs", `${name}.md`), kind: "markdown" }
|
|
60717
61162
|
];
|
|
60718
|
-
const docsSpecExact =
|
|
61163
|
+
const docsSpecExact = join46(repoRoot, "docs", "specs", `SPEC-${name}.md`);
|
|
60719
61164
|
candidates.push({ abs: docsSpecExact, kind: "markdown" });
|
|
60720
61165
|
const checked = candidates.map((c) => relative17(repoRoot, c.abs));
|
|
60721
61166
|
for (const { abs, kind } of candidates.slice(0, 2)) {
|
|
@@ -60729,11 +61174,11 @@ async function searchSpecSource(naxDir, repoRoot, name) {
|
|
|
60729
61174
|
if (await isNonEmptyFile(docsSpecExact)) {
|
|
60730
61175
|
return { source: { kind: "markdown", path: relative17(repoRoot, docsSpecExact) }, checked };
|
|
60731
61176
|
}
|
|
60732
|
-
const docsSpecsDir =
|
|
61177
|
+
const docsSpecsDir = join46(repoRoot, "docs", "specs");
|
|
60733
61178
|
if (existsSync13(docsSpecsDir)) {
|
|
60734
61179
|
const glob = new Bun.Glob(`*${name}*.md`);
|
|
60735
61180
|
for (const match of glob.scanSync({ cwd: docsSpecsDir, absolute: false })) {
|
|
60736
|
-
const abs =
|
|
61181
|
+
const abs = join46(docsSpecsDir, match);
|
|
60737
61182
|
if (await isNonEmptyFile(abs)) {
|
|
60738
61183
|
const relPath = relative17(repoRoot, abs);
|
|
60739
61184
|
if (!checked.includes(relPath))
|
|
@@ -60742,7 +61187,7 @@ async function searchSpecSource(naxDir, repoRoot, name) {
|
|
|
60742
61187
|
}
|
|
60743
61188
|
}
|
|
60744
61189
|
}
|
|
60745
|
-
const prdAbs =
|
|
61190
|
+
const prdAbs = join46(naxDir, "features", name, "prd.json");
|
|
60746
61191
|
const prdRel = relative17(repoRoot, prdAbs);
|
|
60747
61192
|
if (!checked.includes(prdRel))
|
|
60748
61193
|
checked.push(prdRel);
|
|
@@ -60752,14 +61197,14 @@ async function searchSpecSource(naxDir, repoRoot, name) {
|
|
|
60752
61197
|
return { source: null, checked };
|
|
60753
61198
|
}
|
|
60754
61199
|
function discoverCandidates(naxDir) {
|
|
60755
|
-
const featuresDir2 =
|
|
61200
|
+
const featuresDir2 = join46(naxDir, "features");
|
|
60756
61201
|
if (!existsSync13(featuresDir2))
|
|
60757
61202
|
return [];
|
|
60758
61203
|
return readdirSync3(featuresDir2, { withFileTypes: true }).filter((e) => {
|
|
60759
61204
|
if (!e.isDirectory())
|
|
60760
61205
|
return false;
|
|
60761
|
-
const dir =
|
|
60762
|
-
return existsSync13(
|
|
61206
|
+
const dir = join46(featuresDir2, e.name);
|
|
61207
|
+
return existsSync13(join46(dir, "prd.json")) || existsSync13(join46(dir, "spec.md"));
|
|
60763
61208
|
}).map((e) => e.name).sort();
|
|
60764
61209
|
}
|
|
60765
61210
|
async function resolveFeatureSpec(name, workdir) {
|
|
@@ -60770,9 +61215,9 @@ async function resolveFeatureSpec(name, workdir) {
|
|
|
60770
61215
|
message: `not a nax repo: no .nax/config.json found from ${workdir}`
|
|
60771
61216
|
};
|
|
60772
61217
|
}
|
|
60773
|
-
const repoRoot =
|
|
61218
|
+
const repoRoot = join46(naxDir, "..");
|
|
60774
61219
|
if (name !== undefined && (name.startsWith("./") || name.startsWith("/") || name.endsWith(".md"))) {
|
|
60775
|
-
const abs = name.startsWith("/") ? name :
|
|
61220
|
+
const abs = name.startsWith("/") ? name : join46(workdir, name);
|
|
60776
61221
|
if (!existsSync13(abs)) {
|
|
60777
61222
|
return {
|
|
60778
61223
|
status: "missing",
|
|
@@ -60812,7 +61257,7 @@ async function resolveFeatureSpec(name, workdir) {
|
|
|
60812
61257
|
message: `resolved spec: ${source2.path}`
|
|
60813
61258
|
};
|
|
60814
61259
|
}
|
|
60815
|
-
const featureDir2 =
|
|
61260
|
+
const featureDir2 = join46(naxDir, "features", name);
|
|
60816
61261
|
if (existsSync13(featureDir2)) {
|
|
60817
61262
|
return {
|
|
60818
61263
|
status: "missing",
|
|
@@ -60875,9 +61320,9 @@ var init_features_resolve = __esm(() => {
|
|
|
60875
61320
|
|
|
60876
61321
|
// src/context/injector.ts
|
|
60877
61322
|
import { existsSync as existsSync14 } from "fs";
|
|
60878
|
-
import { join as
|
|
61323
|
+
import { join as join47 } from "path";
|
|
60879
61324
|
async function detectNode(workdir) {
|
|
60880
|
-
const pkgPath =
|
|
61325
|
+
const pkgPath = join47(workdir, "package.json");
|
|
60881
61326
|
if (!existsSync14(pkgPath))
|
|
60882
61327
|
return null;
|
|
60883
61328
|
try {
|
|
@@ -60894,7 +61339,7 @@ async function detectNode(workdir) {
|
|
|
60894
61339
|
}
|
|
60895
61340
|
}
|
|
60896
61341
|
async function detectGo(workdir) {
|
|
60897
|
-
const goMod =
|
|
61342
|
+
const goMod = join47(workdir, "go.mod");
|
|
60898
61343
|
if (!existsSync14(goMod))
|
|
60899
61344
|
return null;
|
|
60900
61345
|
try {
|
|
@@ -60918,7 +61363,7 @@ async function detectGo(workdir) {
|
|
|
60918
61363
|
}
|
|
60919
61364
|
}
|
|
60920
61365
|
async function detectRust(workdir) {
|
|
60921
|
-
const cargoPath =
|
|
61366
|
+
const cargoPath = join47(workdir, "Cargo.toml");
|
|
60922
61367
|
if (!existsSync14(cargoPath))
|
|
60923
61368
|
return null;
|
|
60924
61369
|
try {
|
|
@@ -60934,8 +61379,8 @@ async function detectRust(workdir) {
|
|
|
60934
61379
|
}
|
|
60935
61380
|
}
|
|
60936
61381
|
async function detectPython(workdir) {
|
|
60937
|
-
const pyproject =
|
|
60938
|
-
const requirements =
|
|
61382
|
+
const pyproject = join47(workdir, "pyproject.toml");
|
|
61383
|
+
const requirements = join47(workdir, "requirements.txt");
|
|
60939
61384
|
if (!existsSync14(pyproject) && !existsSync14(requirements))
|
|
60940
61385
|
return null;
|
|
60941
61386
|
try {
|
|
@@ -60954,7 +61399,7 @@ async function detectPython(workdir) {
|
|
|
60954
61399
|
}
|
|
60955
61400
|
}
|
|
60956
61401
|
async function detectPhp(workdir) {
|
|
60957
|
-
const composerPath =
|
|
61402
|
+
const composerPath = join47(workdir, "composer.json");
|
|
60958
61403
|
if (!existsSync14(composerPath))
|
|
60959
61404
|
return null;
|
|
60960
61405
|
try {
|
|
@@ -60967,7 +61412,7 @@ async function detectPhp(workdir) {
|
|
|
60967
61412
|
}
|
|
60968
61413
|
}
|
|
60969
61414
|
async function detectRuby(workdir) {
|
|
60970
|
-
const gemfile =
|
|
61415
|
+
const gemfile = join47(workdir, "Gemfile");
|
|
60971
61416
|
if (!existsSync14(gemfile))
|
|
60972
61417
|
return null;
|
|
60973
61418
|
try {
|
|
@@ -60979,9 +61424,9 @@ async function detectRuby(workdir) {
|
|
|
60979
61424
|
}
|
|
60980
61425
|
}
|
|
60981
61426
|
async function detectJvm(workdir) {
|
|
60982
|
-
const pom =
|
|
60983
|
-
const gradle =
|
|
60984
|
-
const gradleKts =
|
|
61427
|
+
const pom = join47(workdir, "pom.xml");
|
|
61428
|
+
const gradle = join47(workdir, "build.gradle");
|
|
61429
|
+
const gradleKts = join47(workdir, "build.gradle.kts");
|
|
60985
61430
|
if (!existsSync14(pom) && !existsSync14(gradle) && !existsSync14(gradleKts))
|
|
60986
61431
|
return null;
|
|
60987
61432
|
try {
|
|
@@ -60989,7 +61434,7 @@ async function detectJvm(workdir) {
|
|
|
60989
61434
|
const content2 = await Bun.file(pom).text();
|
|
60990
61435
|
const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
60991
61436
|
const deps2 = [...content2.matchAll(/<artifactId>([^<]+)<\/artifactId>/g)].map((m) => m[1]).filter((d) => d !== nameMatch?.[1]).slice(0, 10);
|
|
60992
|
-
const lang2 = existsSync14(
|
|
61437
|
+
const lang2 = existsSync14(join47(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
|
|
60993
61438
|
return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
|
|
60994
61439
|
}
|
|
60995
61440
|
const gradleFile = existsSync14(gradleKts) ? gradleKts : gradle;
|
|
@@ -61243,7 +61688,7 @@ var init_windsurf = __esm(() => {
|
|
|
61243
61688
|
|
|
61244
61689
|
// src/context/generator.ts
|
|
61245
61690
|
import { existsSync as existsSync15 } from "fs";
|
|
61246
|
-
import { join as
|
|
61691
|
+
import { join as join48, relative as relative18 } from "path";
|
|
61247
61692
|
async function loadContextContent(options, config2) {
|
|
61248
61693
|
if (!_generatorDeps.existsSync(options.contextPath)) {
|
|
61249
61694
|
throw new Error(`Context file not found: ${options.contextPath}`);
|
|
@@ -61261,7 +61706,7 @@ async function generateFor(agent, options, config2) {
|
|
|
61261
61706
|
try {
|
|
61262
61707
|
const context = await loadContextContent(options, config2);
|
|
61263
61708
|
const content = generator.generate(context);
|
|
61264
|
-
const outputPath =
|
|
61709
|
+
const outputPath = join48(options.outputDir, generator.outputFile);
|
|
61265
61710
|
validateFilePath(outputPath, options.outputDir);
|
|
61266
61711
|
if (!options.dryRun) {
|
|
61267
61712
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -61279,7 +61724,7 @@ async function generateAll(options, config2, agentFilter) {
|
|
|
61279
61724
|
for (const [agentKey, generator] of entries) {
|
|
61280
61725
|
try {
|
|
61281
61726
|
const content = generator.generate(context);
|
|
61282
|
-
const outputPath =
|
|
61727
|
+
const outputPath = join48(options.outputDir, generator.outputFile);
|
|
61283
61728
|
validateFilePath(outputPath, options.outputDir);
|
|
61284
61729
|
if (!options.dryRun) {
|
|
61285
61730
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -61299,7 +61744,7 @@ async function discoverPackages(repoRoot) {
|
|
|
61299
61744
|
const glob = new Bun.Glob(pattern);
|
|
61300
61745
|
for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
|
|
61301
61746
|
const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
|
|
61302
|
-
const pkgAbsolute =
|
|
61747
|
+
const pkgAbsolute = join48(repoRoot, pkgRelative);
|
|
61303
61748
|
if (!seen.has(pkgAbsolute)) {
|
|
61304
61749
|
seen.add(pkgAbsolute);
|
|
61305
61750
|
packages.push(pkgAbsolute);
|
|
@@ -61331,14 +61776,14 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
61331
61776
|
}
|
|
61332
61777
|
}
|
|
61333
61778
|
}
|
|
61334
|
-
const turboPath =
|
|
61779
|
+
const turboPath = join48(repoRoot, "turbo.json");
|
|
61335
61780
|
try {
|
|
61336
61781
|
const turbo = JSON.parse(await _generatorDeps.readTextFile(turboPath));
|
|
61337
61782
|
if (Array.isArray(turbo.packages)) {
|
|
61338
61783
|
await resolveGlobs(turbo.packages);
|
|
61339
61784
|
}
|
|
61340
61785
|
} catch {}
|
|
61341
|
-
const pkgPath =
|
|
61786
|
+
const pkgPath = join48(repoRoot, "package.json");
|
|
61342
61787
|
try {
|
|
61343
61788
|
const pkg = JSON.parse(await _generatorDeps.readTextFile(pkgPath));
|
|
61344
61789
|
const ws = pkg.workspaces;
|
|
@@ -61346,7 +61791,7 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
61346
61791
|
if (patterns.length > 0)
|
|
61347
61792
|
await resolveGlobs(patterns);
|
|
61348
61793
|
} catch {}
|
|
61349
|
-
const pnpmPath =
|
|
61794
|
+
const pnpmPath = join48(repoRoot, "pnpm-workspace.yaml");
|
|
61350
61795
|
try {
|
|
61351
61796
|
const raw = await _generatorDeps.readTextFile(pnpmPath);
|
|
61352
61797
|
const lines = raw.split(`
|
|
@@ -61372,7 +61817,7 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
61372
61817
|
async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
|
|
61373
61818
|
const resolvedRepoRoot = repoRoot ?? packageDir;
|
|
61374
61819
|
const relativePkgPath = relative18(resolvedRepoRoot, packageDir);
|
|
61375
|
-
const contextPath =
|
|
61820
|
+
const contextPath = join48(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
|
|
61376
61821
|
if (!_generatorDeps.existsSync(contextPath)) {
|
|
61377
61822
|
return [
|
|
61378
61823
|
{
|
|
@@ -61441,7 +61886,7 @@ var init_generator2 = __esm(() => {
|
|
|
61441
61886
|
|
|
61442
61887
|
// src/cli/generate.ts
|
|
61443
61888
|
import { existsSync as existsSync16 } from "fs";
|
|
61444
|
-
import { join as
|
|
61889
|
+
import { join as join49 } from "path";
|
|
61445
61890
|
async function generateCommand(options) {
|
|
61446
61891
|
const workdir = options.dir ?? process.cwd();
|
|
61447
61892
|
const dryRun = options.dryRun ?? false;
|
|
@@ -61486,7 +61931,7 @@ async function generateCommand(options) {
|
|
|
61486
61931
|
if (!isRelativeAndSafe(options.package)) {
|
|
61487
61932
|
throw new NaxError(`generateCommand: package "${options.package}" is not a safe relative path (must be non-empty, relative, and free of ".." segments)`, "INVALID_PACKAGE_PATH", { stage: "generate", package: options.package });
|
|
61488
61933
|
}
|
|
61489
|
-
const packageDir =
|
|
61934
|
+
const packageDir = join49(workdir, options.package);
|
|
61490
61935
|
if (dryRun) {
|
|
61491
61936
|
console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
|
|
61492
61937
|
}
|
|
@@ -61506,8 +61951,8 @@ async function generateCommand(options) {
|
|
|
61506
61951
|
process.exit(1);
|
|
61507
61952
|
return;
|
|
61508
61953
|
}
|
|
61509
|
-
const contextPath = options.context ?
|
|
61510
|
-
const outputDir = options.output ?
|
|
61954
|
+
const contextPath = options.context ? join49(workdir, options.context) : join49(workdir, ".nax/context.md");
|
|
61955
|
+
const outputDir = options.output ? join49(workdir, options.output) : workdir;
|
|
61511
61956
|
const autoInject = !options.noAutoInject;
|
|
61512
61957
|
if (!existsSync16(contextPath)) {
|
|
61513
61958
|
console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
|
|
@@ -61621,11 +62066,11 @@ var init_generate = __esm(() => {
|
|
|
61621
62066
|
});
|
|
61622
62067
|
|
|
61623
62068
|
// src/utils/nax-project-root.ts
|
|
61624
|
-
import { dirname as dirname11, join as
|
|
62069
|
+
import { dirname as dirname11, join as join50, resolve as resolve19 } from "path";
|
|
61625
62070
|
async function findNaxProjectRoot(startDir) {
|
|
61626
62071
|
let dir = resolve19(startDir);
|
|
61627
62072
|
for (let depth = 0;depth < MAX_NAX_WALK_DEPTH; depth++) {
|
|
61628
|
-
if (await _naxProjectRootDeps.exists(
|
|
62073
|
+
if (await _naxProjectRootDeps.exists(join50(dir, ".nax", "config.json"))) {
|
|
61629
62074
|
return dir;
|
|
61630
62075
|
}
|
|
61631
62076
|
const parent = dirname11(dir);
|
|
@@ -61649,7 +62094,7 @@ var package_default;
|
|
|
61649
62094
|
var init_package = __esm(() => {
|
|
61650
62095
|
package_default = {
|
|
61651
62096
|
name: "@nathapp/nax",
|
|
61652
|
-
version: "0.82.0-canary.
|
|
62097
|
+
version: "0.82.0-canary.7",
|
|
61653
62098
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
61654
62099
|
type: "module",
|
|
61655
62100
|
bin: {
|
|
@@ -61770,8 +62215,8 @@ var init_version = __esm(() => {
|
|
|
61770
62215
|
NAX_VERSION = package_default.version;
|
|
61771
62216
|
NAX_COMMIT = (() => {
|
|
61772
62217
|
try {
|
|
61773
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
61774
|
-
return "
|
|
62218
|
+
if (/^[0-9a-f]{6,10}$/.test("21dacecf"))
|
|
62219
|
+
return "21dacecf";
|
|
61775
62220
|
} catch {}
|
|
61776
62221
|
try {
|
|
61777
62222
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -61791,7 +62236,7 @@ var init_version = __esm(() => {
|
|
|
61791
62236
|
|
|
61792
62237
|
// src/review/review-audit.ts
|
|
61793
62238
|
import { mkdir as mkdir9 } from "fs/promises";
|
|
61794
|
-
import { join as
|
|
62239
|
+
import { join as join51 } from "path";
|
|
61795
62240
|
function auditKey(reviewer, storyId) {
|
|
61796
62241
|
return `${reviewer}:${storyId ?? "_feature"}`;
|
|
61797
62242
|
}
|
|
@@ -61827,15 +62272,15 @@ function toPersistedEntry(entry, epochMs) {
|
|
|
61827
62272
|
async function persistReviewAudit(entry) {
|
|
61828
62273
|
let resolvedDir;
|
|
61829
62274
|
if (entry.outputDir) {
|
|
61830
|
-
resolvedDir =
|
|
62275
|
+
resolvedDir = join51(entry.outputDir, "review-audit", entry.featureName ?? "_unknown");
|
|
61831
62276
|
} else {
|
|
61832
62277
|
const projectRoot = entry.projectDir ?? await _reviewAuditDeps.findNaxProjectRoot(entry.workdir);
|
|
61833
|
-
resolvedDir =
|
|
62278
|
+
resolvedDir = join51(projectRoot, ".nax", "review-audit", entry.featureName ?? "_unknown");
|
|
61834
62279
|
}
|
|
61835
62280
|
await _reviewAuditDeps.mkdir(resolvedDir);
|
|
61836
62281
|
const epochMs = _reviewAuditDeps.now();
|
|
61837
62282
|
const filename = `${epochMs}-${entry.sessionName}.json`;
|
|
61838
|
-
await _reviewAuditDeps.writeFile(
|
|
62283
|
+
await _reviewAuditDeps.writeFile(join51(resolvedDir, filename), toPersistedEntry(entry, epochMs));
|
|
61839
62284
|
}
|
|
61840
62285
|
function createNoOpReviewAuditor() {
|
|
61841
62286
|
return {
|
|
@@ -61954,283 +62399,6 @@ var init_agent_stream_events = __esm(() => {
|
|
|
61954
62399
|
init_logger2();
|
|
61955
62400
|
});
|
|
61956
62401
|
|
|
61957
|
-
// src/runtime/cost-aggregator.ts
|
|
61958
|
-
import { mkdirSync as mkdirSync3 } from "fs";
|
|
61959
|
-
import { join as join51 } from "path";
|
|
61960
|
-
function makeCorrelationId() {
|
|
61961
|
-
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
61962
|
-
}
|
|
61963
|
-
function createNoOpCostAggregator() {
|
|
61964
|
-
return {
|
|
61965
|
-
record() {},
|
|
61966
|
-
recordError() {},
|
|
61967
|
-
recordOperationSummary() {},
|
|
61968
|
-
snapshot() {
|
|
61969
|
-
return EMPTY_SNAPSHOT;
|
|
61970
|
-
},
|
|
61971
|
-
byAgent() {
|
|
61972
|
-
return {};
|
|
61973
|
-
},
|
|
61974
|
-
byStage() {
|
|
61975
|
-
return {};
|
|
61976
|
-
},
|
|
61977
|
-
byStory() {
|
|
61978
|
-
return {};
|
|
61979
|
-
},
|
|
61980
|
-
byCall() {
|
|
61981
|
-
return {};
|
|
61982
|
-
},
|
|
61983
|
-
byScope() {
|
|
61984
|
-
return {};
|
|
61985
|
-
},
|
|
61986
|
-
openScope(scopeId) {
|
|
61987
|
-
return {
|
|
61988
|
-
scopeId: scopeId ?? makeCorrelationId(),
|
|
61989
|
-
snapshot: () => EMPTY_SNAPSHOT,
|
|
61990
|
-
close: () => {}
|
|
61991
|
-
};
|
|
61992
|
-
},
|
|
61993
|
-
async drain() {}
|
|
61994
|
-
};
|
|
61995
|
-
}
|
|
61996
|
-
function emptySnap() {
|
|
61997
|
-
return {
|
|
61998
|
-
totalCostUsd: 0,
|
|
61999
|
-
totalEstimatedCostUsd: 0,
|
|
62000
|
-
totalExactCostUsd: 0,
|
|
62001
|
-
totalInputTokens: 0,
|
|
62002
|
-
totalOutputTokens: 0,
|
|
62003
|
-
totalCacheReadTokens: 0,
|
|
62004
|
-
totalCacheWriteTokens: 0,
|
|
62005
|
-
tokenUsageCount: 0,
|
|
62006
|
-
callCount: 0,
|
|
62007
|
-
errorCount: 0,
|
|
62008
|
-
totalErrorCostUsd: 0
|
|
62009
|
-
};
|
|
62010
|
-
}
|
|
62011
|
-
function accumulate(snap, e) {
|
|
62012
|
-
const tokens = e.tokens;
|
|
62013
|
-
return {
|
|
62014
|
-
totalCostUsd: snap.totalCostUsd + e.costUsd,
|
|
62015
|
-
totalEstimatedCostUsd: snap.totalEstimatedCostUsd + e.estimatedCostUsd,
|
|
62016
|
-
totalExactCostUsd: snap.totalExactCostUsd + e.exactCostUsd,
|
|
62017
|
-
totalInputTokens: snap.totalInputTokens + (tokens?.input ?? 0),
|
|
62018
|
-
totalOutputTokens: snap.totalOutputTokens + (tokens?.output ?? 0),
|
|
62019
|
-
totalCacheReadTokens: (snap.totalCacheReadTokens ?? 0) + (tokens?.cacheRead ?? 0),
|
|
62020
|
-
totalCacheWriteTokens: (snap.totalCacheWriteTokens ?? 0) + (tokens?.cacheWrite ?? 0),
|
|
62021
|
-
tokenUsageCount: (snap.tokenUsageCount ?? 0) + (tokens ? 1 : 0),
|
|
62022
|
-
callCount: snap.callCount + 1,
|
|
62023
|
-
errorCount: snap.errorCount,
|
|
62024
|
-
totalErrorCostUsd: snap.totalErrorCostUsd
|
|
62025
|
-
};
|
|
62026
|
-
}
|
|
62027
|
-
function accumulateError(snap, e) {
|
|
62028
|
-
return {
|
|
62029
|
-
...snap,
|
|
62030
|
-
errorCount: snap.errorCount + 1,
|
|
62031
|
-
totalErrorCostUsd: snap.totalErrorCostUsd + (e?.costUsd ?? 0)
|
|
62032
|
-
};
|
|
62033
|
-
}
|
|
62034
|
-
|
|
62035
|
-
class CostAggregator {
|
|
62036
|
-
_runId;
|
|
62037
|
-
_drainDir;
|
|
62038
|
-
_events = [];
|
|
62039
|
-
_errors = [];
|
|
62040
|
-
_draining = false;
|
|
62041
|
-
_inFlightEvents = [];
|
|
62042
|
-
_inFlightErrors = [];
|
|
62043
|
-
_openScopes = new Set;
|
|
62044
|
-
constructor(_runId, _drainDir) {
|
|
62045
|
-
this._runId = _runId;
|
|
62046
|
-
this._drainDir = _drainDir;
|
|
62047
|
-
}
|
|
62048
|
-
record(event) {
|
|
62049
|
-
if (this._draining) {
|
|
62050
|
-
this._inFlightEvents.push(event);
|
|
62051
|
-
return;
|
|
62052
|
-
}
|
|
62053
|
-
this._events.push(event);
|
|
62054
|
-
}
|
|
62055
|
-
recordError(event) {
|
|
62056
|
-
if (this._draining) {
|
|
62057
|
-
this._inFlightErrors.push(event);
|
|
62058
|
-
return;
|
|
62059
|
-
}
|
|
62060
|
-
this._errors.push(event);
|
|
62061
|
-
}
|
|
62062
|
-
recordOperationSummary(_event) {}
|
|
62063
|
-
snapshot() {
|
|
62064
|
-
const allEvents = [...this._events, ...this._inFlightEvents];
|
|
62065
|
-
const allErrors = [...this._errors, ...this._inFlightErrors];
|
|
62066
|
-
const eventReduced = allEvents.reduce(accumulate, emptySnap());
|
|
62067
|
-
return allErrors.reduce((snap, e) => accumulateError(snap, e), eventReduced);
|
|
62068
|
-
}
|
|
62069
|
-
byAgent() {
|
|
62070
|
-
const m = {};
|
|
62071
|
-
for (const e of this._events)
|
|
62072
|
-
m[e.agentName] = accumulate(m[e.agentName] ?? emptySnap(), e);
|
|
62073
|
-
for (const e of this._inFlightEvents)
|
|
62074
|
-
m[e.agentName] = accumulate(m[e.agentName] ?? emptySnap(), e);
|
|
62075
|
-
for (const e of this._errors)
|
|
62076
|
-
m[e.agentName] = accumulateError(m[e.agentName] ?? emptySnap(), e);
|
|
62077
|
-
for (const e of this._inFlightErrors)
|
|
62078
|
-
m[e.agentName] = accumulateError(m[e.agentName] ?? emptySnap(), e);
|
|
62079
|
-
return m;
|
|
62080
|
-
}
|
|
62081
|
-
byStage() {
|
|
62082
|
-
const m = {};
|
|
62083
|
-
for (const e of this._events) {
|
|
62084
|
-
const k = e.stage ?? "unknown";
|
|
62085
|
-
m[k] = accumulate(m[k] ?? emptySnap(), e);
|
|
62086
|
-
}
|
|
62087
|
-
for (const e of this._inFlightEvents) {
|
|
62088
|
-
const k = e.stage ?? "unknown";
|
|
62089
|
-
m[k] = accumulate(m[k] ?? emptySnap(), e);
|
|
62090
|
-
}
|
|
62091
|
-
for (const e of this._errors) {
|
|
62092
|
-
const k = e.stage ?? "unknown";
|
|
62093
|
-
m[k] = accumulateError(m[k] ?? emptySnap(), e);
|
|
62094
|
-
}
|
|
62095
|
-
for (const e of this._inFlightErrors) {
|
|
62096
|
-
const k = e.stage ?? "unknown";
|
|
62097
|
-
m[k] = accumulateError(m[k] ?? emptySnap(), e);
|
|
62098
|
-
}
|
|
62099
|
-
return m;
|
|
62100
|
-
}
|
|
62101
|
-
byStory() {
|
|
62102
|
-
const m = {};
|
|
62103
|
-
for (const e of this._events) {
|
|
62104
|
-
const k = e.storyId ?? "unknown";
|
|
62105
|
-
m[k] = accumulate(m[k] ?? emptySnap(), e);
|
|
62106
|
-
}
|
|
62107
|
-
for (const e of this._inFlightEvents) {
|
|
62108
|
-
const k = e.storyId ?? "unknown";
|
|
62109
|
-
m[k] = accumulate(m[k] ?? emptySnap(), e);
|
|
62110
|
-
}
|
|
62111
|
-
for (const e of this._errors) {
|
|
62112
|
-
const k = e.storyId ?? "unknown";
|
|
62113
|
-
m[k] = accumulateError(m[k] ?? emptySnap(), e);
|
|
62114
|
-
}
|
|
62115
|
-
for (const e of this._inFlightErrors) {
|
|
62116
|
-
const k = e.storyId ?? "unknown";
|
|
62117
|
-
m[k] = accumulateError(m[k] ?? emptySnap(), e);
|
|
62118
|
-
}
|
|
62119
|
-
return m;
|
|
62120
|
-
}
|
|
62121
|
-
byCall() {
|
|
62122
|
-
const m = {};
|
|
62123
|
-
for (const e of [...this._events, ...this._inFlightEvents]) {
|
|
62124
|
-
if (e.callId === undefined)
|
|
62125
|
-
continue;
|
|
62126
|
-
m[e.callId] = accumulate(m[e.callId] ?? emptySnap(), e);
|
|
62127
|
-
}
|
|
62128
|
-
for (const e of [...this._errors, ...this._inFlightErrors]) {
|
|
62129
|
-
if (e.callId === undefined)
|
|
62130
|
-
continue;
|
|
62131
|
-
m[e.callId] = accumulateError(m[e.callId] ?? emptySnap(), e);
|
|
62132
|
-
}
|
|
62133
|
-
return m;
|
|
62134
|
-
}
|
|
62135
|
-
byScope() {
|
|
62136
|
-
const m = {};
|
|
62137
|
-
for (const e of [...this._events, ...this._inFlightEvents]) {
|
|
62138
|
-
if (e.scopeId === undefined)
|
|
62139
|
-
continue;
|
|
62140
|
-
m[e.scopeId] = accumulate(m[e.scopeId] ?? emptySnap(), e);
|
|
62141
|
-
}
|
|
62142
|
-
for (const e of [...this._errors, ...this._inFlightErrors]) {
|
|
62143
|
-
if (e.scopeId === undefined)
|
|
62144
|
-
continue;
|
|
62145
|
-
m[e.scopeId] = accumulateError(m[e.scopeId] ?? emptySnap(), e);
|
|
62146
|
-
}
|
|
62147
|
-
return m;
|
|
62148
|
-
}
|
|
62149
|
-
openScope(scopeId) {
|
|
62150
|
-
const id = scopeId ?? _costAggDeps.newCorrelationId();
|
|
62151
|
-
this._openScopes.add(id);
|
|
62152
|
-
let closed = false;
|
|
62153
|
-
return {
|
|
62154
|
-
scopeId: id,
|
|
62155
|
-
snapshot: () => {
|
|
62156
|
-
const matching = [...this._events, ...this._inFlightEvents].filter((e) => e.scopeId === id);
|
|
62157
|
-
const matchingErrors = [...this._errors, ...this._inFlightErrors].filter((e) => e.scopeId === id);
|
|
62158
|
-
const eventSnapshot = matching.reduce(accumulate, emptySnap());
|
|
62159
|
-
return matchingErrors.reduce((snap, e) => accumulateError(snap, e), eventSnapshot);
|
|
62160
|
-
},
|
|
62161
|
-
close: () => {
|
|
62162
|
-
if (closed)
|
|
62163
|
-
return;
|
|
62164
|
-
closed = true;
|
|
62165
|
-
this._openScopes.delete(id);
|
|
62166
|
-
}
|
|
62167
|
-
};
|
|
62168
|
-
}
|
|
62169
|
-
async drain() {
|
|
62170
|
-
const openScopeCount = this._openScopes.size;
|
|
62171
|
-
if (openScopeCount > 0) {
|
|
62172
|
-
_costAggDeps.getSafeLogger()?.warn("cost-aggregator", "drain called with open scopes", { openScopeCount });
|
|
62173
|
-
}
|
|
62174
|
-
this._draining = true;
|
|
62175
|
-
try {
|
|
62176
|
-
let committed = [...this._events.splice(0), ...this._errors.splice(0)];
|
|
62177
|
-
if (committed.length === 0 && this._inFlightEvents.length === 0 && this._inFlightErrors.length === 0) {
|
|
62178
|
-
return;
|
|
62179
|
-
}
|
|
62180
|
-
mkdirSync3(this._drainDir, { recursive: true });
|
|
62181
|
-
const path11 = join51(this._drainDir, `${this._runId}.jsonl`);
|
|
62182
|
-
let first = true;
|
|
62183
|
-
let pass = 0;
|
|
62184
|
-
while (first || this._inFlightEvents.length > 0 || this._inFlightErrors.length > 0) {
|
|
62185
|
-
if (++pass > MAX_DRAIN_PASSES) {
|
|
62186
|
-
_costAggDeps.getSafeLogger()?.warn("cost-aggregator", "drain exceeded max passes \u2014 late events keep arriving", {
|
|
62187
|
-
pass,
|
|
62188
|
-
inFlightEvents: this._inFlightEvents.length,
|
|
62189
|
-
inFlightErrors: this._inFlightErrors.length
|
|
62190
|
-
});
|
|
62191
|
-
break;
|
|
62192
|
-
}
|
|
62193
|
-
first = false;
|
|
62194
|
-
const late = [...this._inFlightEvents.splice(0), ...this._inFlightErrors.splice(0)];
|
|
62195
|
-
committed = [...committed, ...late].sort((a, b) => a.ts - b.ts);
|
|
62196
|
-
await _costAggDeps.write(path11, `${committed.map((e) => JSON.stringify(e)).join(`
|
|
62197
|
-
`)}
|
|
62198
|
-
`);
|
|
62199
|
-
}
|
|
62200
|
-
const committedEvents = committed.filter((e) => !(("kind" in e) && e.kind === "error"));
|
|
62201
|
-
const committedErrors = committed.filter((e) => ("kind" in e) && e.kind === "error");
|
|
62202
|
-
this._events.length = 0;
|
|
62203
|
-
this._events.push(...committedEvents);
|
|
62204
|
-
this._errors.length = 0;
|
|
62205
|
-
this._errors.push(...committedErrors);
|
|
62206
|
-
} finally {
|
|
62207
|
-
this._draining = false;
|
|
62208
|
-
}
|
|
62209
|
-
}
|
|
62210
|
-
}
|
|
62211
|
-
var MAX_DRAIN_PASSES = 20, EMPTY_SNAPSHOT, _costAggDeps;
|
|
62212
|
-
var init_cost_aggregator = __esm(() => {
|
|
62213
|
-
init_logger2();
|
|
62214
|
-
EMPTY_SNAPSHOT = {
|
|
62215
|
-
totalCostUsd: 0,
|
|
62216
|
-
totalEstimatedCostUsd: 0,
|
|
62217
|
-
totalExactCostUsd: 0,
|
|
62218
|
-
totalInputTokens: 0,
|
|
62219
|
-
totalOutputTokens: 0,
|
|
62220
|
-
totalCacheReadTokens: 0,
|
|
62221
|
-
totalCacheWriteTokens: 0,
|
|
62222
|
-
tokenUsageCount: 0,
|
|
62223
|
-
callCount: 0,
|
|
62224
|
-
errorCount: 0,
|
|
62225
|
-
totalErrorCostUsd: 0
|
|
62226
|
-
};
|
|
62227
|
-
_costAggDeps = {
|
|
62228
|
-
write: (path11, data) => Bun.write(path11, data),
|
|
62229
|
-
getSafeLogger: () => getSafeLogger(),
|
|
62230
|
-
newCorrelationId: makeCorrelationId
|
|
62231
|
-
};
|
|
62232
|
-
});
|
|
62233
|
-
|
|
62234
62402
|
// src/runtime/middleware/agent-stream-logging.ts
|
|
62235
62403
|
function attachAgentStreamLogging(bus, runId) {
|
|
62236
62404
|
const activeCalls = new Map;
|
|
@@ -63653,6 +63821,8 @@ __export(exports_runtime, {
|
|
|
63653
63821
|
projectOutputDir: () => projectOutputDir,
|
|
63654
63822
|
readProjectIdentity: () => readProjectIdentity,
|
|
63655
63823
|
resolveIdleWatchdogSettings: () => resolveIdleWatchdogSettings,
|
|
63824
|
+
storySpendUsd: () => storySpendUsd,
|
|
63825
|
+
totalSpendUsd: () => totalSpendUsd,
|
|
63656
63826
|
writeProjectIdentity: () => writeProjectIdentity
|
|
63657
63827
|
});
|
|
63658
63828
|
import { basename as basename9, join as join53 } from "path";
|
|
@@ -63705,7 +63875,8 @@ function createRuntime(config2, workdir, opts) {
|
|
|
63705
63875
|
runId,
|
|
63706
63876
|
sendPrompt: (handle, prompt, sendOpts) => sessionManager.sendPrompt(handle, prompt, sendOpts),
|
|
63707
63877
|
runHop: createSessionRunHop(sessionManager, () => agentManager),
|
|
63708
|
-
dispatchEvents
|
|
63878
|
+
dispatchEvents,
|
|
63879
|
+
models: config2.models
|
|
63709
63880
|
};
|
|
63710
63881
|
if (opts?.agentManager instanceof AgentManager) {
|
|
63711
63882
|
opts.agentManager.configureRuntime({ ...agentManagerOpts, pidRegistry });
|
|
@@ -66115,7 +66286,10 @@ class DebateRunner {
|
|
|
66115
66286
|
resolverCallContext: { ...this.ctx, scopeId: resolverScope.scopeId }
|
|
66116
66287
|
};
|
|
66117
66288
|
const result = await runner(ctxWithScopes, prompt);
|
|
66118
|
-
return {
|
|
66289
|
+
return {
|
|
66290
|
+
...result,
|
|
66291
|
+
totalCostUsd: totalSpendUsd(debaterScope.snapshot()) + totalSpendUsd(resolverScope.snapshot())
|
|
66292
|
+
};
|
|
66119
66293
|
} finally {
|
|
66120
66294
|
debaterScope.close();
|
|
66121
66295
|
resolverScope.close();
|
|
@@ -66153,7 +66327,7 @@ class DebateRunner {
|
|
|
66153
66327
|
debaters: resolved.map((r) => r.debater.agent)
|
|
66154
66328
|
});
|
|
66155
66329
|
const concurrencyLimit = this.config?.debate?.maxConcurrentDebaters ?? 2;
|
|
66156
|
-
const scopeTotal = () => prePhaseScope.snapshot()
|
|
66330
|
+
const scopeTotal = () => totalSpendUsd(prePhaseScope.snapshot()) + totalSpendUsd(debaterScope.snapshot()) + totalSpendUsd(resolverScope.snapshot()) + totalSpendUsd(verifierScope.snapshot());
|
|
66157
66331
|
let taskContext = prompt;
|
|
66158
66332
|
let preDebateManifestSection;
|
|
66159
66333
|
if (config2.preDebatePhase) {
|
|
@@ -71238,7 +71412,8 @@ function collectFixCycleIteration(context, entry, data) {
|
|
|
71238
71412
|
outcome,
|
|
71239
71413
|
findingsBefore: numberValue(data.findingsBefore, 0),
|
|
71240
71414
|
findingsAfter: numberValue(data.findingsAfter, 0),
|
|
71241
|
-
costUsd: numberValue(data.costUsd, 0)
|
|
71415
|
+
costUsd: numberValue(data.costUsd, 0),
|
|
71416
|
+
errorCostUsd: numberValue(data.errorCostUsd, 0)
|
|
71242
71417
|
}
|
|
71243
71418
|
};
|
|
71244
71419
|
}
|
|
@@ -76410,7 +76585,8 @@ async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = fa
|
|
|
76410
76585
|
throw err;
|
|
76411
76586
|
} finally {
|
|
76412
76587
|
const snapshot = scope.snapshot();
|
|
76413
|
-
|
|
76588
|
+
const phaseSpend = totalSpendUsd(snapshot);
|
|
76589
|
+
phaseCosts[opName] = (phaseCosts[opName] ?? 0) + phaseSpend;
|
|
76414
76590
|
scope.close();
|
|
76415
76591
|
if (ctx.storyId) {
|
|
76416
76592
|
const phaseDetails = buildPhaseDetails(opName, phaseOutputs[opName], isThreeSession, ctx.packageView.config, advIterationBefore, ctx.fixStrategy);
|
|
@@ -76420,7 +76596,8 @@ async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = fa
|
|
|
76420
76596
|
phase: opName,
|
|
76421
76597
|
outcome,
|
|
76422
76598
|
durationMs: Date.now() - phaseStartedAt,
|
|
76423
|
-
costUsd:
|
|
76599
|
+
costUsd: phaseSpend,
|
|
76600
|
+
...snapshot.totalErrorCostUsd > 0 ? { errorCostUsd: snapshot.totalErrorCostUsd } : {},
|
|
76424
76601
|
...ctx.phaseTelemetry ? {
|
|
76425
76602
|
tier: ctx.phaseTelemetry.tier,
|
|
76426
76603
|
testStrategy: ctx.phaseTelemetry.testStrategy,
|
|
@@ -76525,6 +76702,7 @@ var init_run_phase = __esm(() => {
|
|
|
76525
76702
|
init_operations();
|
|
76526
76703
|
init_pipeline2();
|
|
76527
76704
|
init_review2();
|
|
76705
|
+
init_runtime2();
|
|
76528
76706
|
init_git();
|
|
76529
76707
|
init_resume_plan();
|
|
76530
76708
|
init_non_blocking_fix();
|
|
@@ -78319,11 +78497,13 @@ async function handleNoTierAvailable(ctx, failureCategory) {
|
|
|
78319
78497
|
if (ctx.featureDir) {
|
|
78320
78498
|
await appendProgress(ctx.featureDir, ctx.story.id, "paused", `${ctx.story.title} \u2014 Execution stopped (needs human review)`);
|
|
78321
78499
|
}
|
|
78500
|
+
const spend2 = storySpendUsd(ctx.runtime?.costAggregator, ctx.story.id, ctx.totalCost);
|
|
78322
78501
|
pipelineEventBus.emit({
|
|
78323
78502
|
type: "story:paused",
|
|
78324
78503
|
storyId: ctx.story.id,
|
|
78325
78504
|
reason: pauseReason,
|
|
78326
|
-
cost:
|
|
78505
|
+
cost: spend2.cost,
|
|
78506
|
+
...spend2.errorCostUsd > 0 ? { errorCostUsd: spend2.errorCostUsd } : {}
|
|
78327
78507
|
});
|
|
78328
78508
|
return { outcome: "paused", prdDirty: true, prd: pausedPrd };
|
|
78329
78509
|
}
|
|
@@ -78336,13 +78516,15 @@ async function handleNoTierAvailable(ctx, failureCategory) {
|
|
|
78336
78516
|
if (ctx.featureDir) {
|
|
78337
78517
|
await appendProgress(ctx.featureDir, ctx.story.id, "failed", `${ctx.story.title} \u2014 Execution failed`);
|
|
78338
78518
|
}
|
|
78519
|
+
const spend = storySpendUsd(ctx.runtime?.costAggregator, ctx.story.id, ctx.totalCost);
|
|
78339
78520
|
pipelineEventBus.emit({
|
|
78340
78521
|
type: "story:failed",
|
|
78341
78522
|
storyId: ctx.story.id,
|
|
78342
78523
|
story: { id: ctx.story.id, title: ctx.story.title, status: ctx.story.status, attempts: ctx.story.attempts },
|
|
78343
78524
|
reason: "Execution failed",
|
|
78344
78525
|
countsTowardEscalation: true,
|
|
78345
|
-
cost:
|
|
78526
|
+
cost: spend.cost,
|
|
78527
|
+
...spend.errorCostUsd > 0 ? { errorCostUsd: spend.errorCostUsd } : {}
|
|
78346
78528
|
});
|
|
78347
78529
|
return { outcome: "failed", prdDirty: true, prd: failedPrd };
|
|
78348
78530
|
}
|
|
@@ -78362,11 +78544,13 @@ async function handleMaxAttemptsReached(ctx, failureCategory) {
|
|
|
78362
78544
|
if (ctx.featureDir) {
|
|
78363
78545
|
await appendProgress(ctx.featureDir, ctx.story.id, "paused", `${ctx.story.title} \u2014 Max attempts reached (needs human review)`);
|
|
78364
78546
|
}
|
|
78547
|
+
const spend2 = storySpendUsd(ctx.runtime?.costAggregator, ctx.story.id, ctx.totalCost);
|
|
78365
78548
|
pipelineEventBus.emit({
|
|
78366
78549
|
type: "story:paused",
|
|
78367
78550
|
storyId: ctx.story.id,
|
|
78368
78551
|
reason: pauseReason,
|
|
78369
|
-
cost:
|
|
78552
|
+
cost: spend2.cost,
|
|
78553
|
+
...spend2.errorCostUsd > 0 ? { errorCostUsd: spend2.errorCostUsd } : {}
|
|
78370
78554
|
});
|
|
78371
78555
|
return { outcome: "paused", prdDirty: true, prd: pausedPrd };
|
|
78372
78556
|
}
|
|
@@ -78380,13 +78564,15 @@ async function handleMaxAttemptsReached(ctx, failureCategory) {
|
|
|
78380
78564
|
if (ctx.featureDir) {
|
|
78381
78565
|
await appendProgress(ctx.featureDir, ctx.story.id, "failed", `${ctx.story.title} \u2014 Max attempts reached`);
|
|
78382
78566
|
}
|
|
78567
|
+
const spend = storySpendUsd(ctx.runtime?.costAggregator, ctx.story.id, ctx.totalCost);
|
|
78383
78568
|
pipelineEventBus.emit({
|
|
78384
78569
|
type: "story:failed",
|
|
78385
78570
|
storyId: ctx.story.id,
|
|
78386
78571
|
story: { id: ctx.story.id, title: ctx.story.title, status: ctx.story.status, attempts: ctx.story.attempts },
|
|
78387
78572
|
reason: "Max attempts reached",
|
|
78388
78573
|
countsTowardEscalation: true,
|
|
78389
|
-
cost:
|
|
78574
|
+
cost: spend.cost,
|
|
78575
|
+
...spend.errorCostUsd > 0 ? { errorCostUsd: spend.errorCostUsd } : {}
|
|
78390
78576
|
});
|
|
78391
78577
|
return { outcome: "failed", prdDirty: true, prd: failedPrd };
|
|
78392
78578
|
}
|
|
@@ -78394,6 +78580,7 @@ var init_tier_outcome = __esm(() => {
|
|
|
78394
78580
|
init_logger2();
|
|
78395
78581
|
init_pipeline2();
|
|
78396
78582
|
init_prd();
|
|
78583
|
+
init_runtime2();
|
|
78397
78584
|
init_progress();
|
|
78398
78585
|
init_quote_integrity();
|
|
78399
78586
|
init_tier_escalation();
|
|
@@ -78536,6 +78723,7 @@ async function preIterationTierCheck(story, routing, config2, prd, prdPath, feat
|
|
|
78536
78723
|
await appendProgress(featureDir2, story.id, "failed", `${story.title} \u2014 All tiers exhausted`);
|
|
78537
78724
|
}
|
|
78538
78725
|
const failedStory = failedPrd.userStories.find((s) => s.id === story.id) ?? story;
|
|
78726
|
+
const spend = storySpendUsd(runtime?.costAggregator, story.id, totalCost);
|
|
78539
78727
|
pipelineEventBus.emit({
|
|
78540
78728
|
type: "story:failed",
|
|
78541
78729
|
storyId: story.id,
|
|
@@ -78544,7 +78732,8 @@ async function preIterationTierCheck(story, routing, config2, prd, prdPath, feat
|
|
|
78544
78732
|
countsTowardEscalation: true,
|
|
78545
78733
|
feature,
|
|
78546
78734
|
attempts: failedStory.attempts,
|
|
78547
|
-
cost:
|
|
78735
|
+
cost: spend.cost,
|
|
78736
|
+
...spend.errorCostUsd > 0 ? { errorCostUsd: spend.errorCostUsd } : {}
|
|
78548
78737
|
});
|
|
78549
78738
|
return { shouldSkipIteration: true, prdDirty: true, prd: failedPrd };
|
|
78550
78739
|
}
|
|
@@ -78669,6 +78858,7 @@ var init_tier_escalation = __esm(() => {
|
|
|
78669
78858
|
init_logger2();
|
|
78670
78859
|
init_pipeline2();
|
|
78671
78860
|
init_prd();
|
|
78861
|
+
init_runtime2();
|
|
78672
78862
|
init_escalation();
|
|
78673
78863
|
init_progress();
|
|
78674
78864
|
init_quote_integrity();
|
|
@@ -79873,6 +80063,7 @@ async function failStoryAfterMerge(ctx, prd, reason) {
|
|
|
79873
80063
|
if (ctx.featureDir) {
|
|
79874
80064
|
await appendProgress(ctx.featureDir, ctx.story.id, "failed", `${ctx.story.title} \u2014 ${reason}`);
|
|
79875
80065
|
}
|
|
80066
|
+
const spend = storySpendUsd(ctx.runtime.costAggregator, ctx.story.id, ctx.totalCost);
|
|
79876
80067
|
pipelineEventBus.emit({
|
|
79877
80068
|
type: "story:failed",
|
|
79878
80069
|
storyId: ctx.story.id,
|
|
@@ -79881,7 +80072,8 @@ async function failStoryAfterMerge(ctx, prd, reason) {
|
|
|
79881
80072
|
countsTowardEscalation: false,
|
|
79882
80073
|
feature: ctx.feature,
|
|
79883
80074
|
attempts: ctx.story.attempts,
|
|
79884
|
-
cost:
|
|
80075
|
+
cost: spend.cost,
|
|
80076
|
+
...spend.errorCostUsd > 0 ? { errorCostUsd: spend.errorCostUsd } : {}
|
|
79885
80077
|
});
|
|
79886
80078
|
}
|
|
79887
80079
|
function filterOutputFiles(files) {
|
|
@@ -80014,11 +80206,13 @@ async function handlePipelineFailure(ctx, pipelineResult) {
|
|
|
80014
80206
|
if (hasWorktree(ctx.workdir, ctx.story.id)) {
|
|
80015
80207
|
await removeWorktreeDirectory(ctx.workdir, ctx.story.id);
|
|
80016
80208
|
}
|
|
80209
|
+
const spend = storySpendUsd(ctx.runtime.costAggregator, ctx.story.id, ctx.totalCost);
|
|
80017
80210
|
pipelineEventBus.emit({
|
|
80018
80211
|
type: "story:paused",
|
|
80019
80212
|
storyId: ctx.story.id,
|
|
80020
80213
|
reason: pipelineResult.reason || "Pipeline paused",
|
|
80021
|
-
cost:
|
|
80214
|
+
cost: spend.cost,
|
|
80215
|
+
...spend.errorCostUsd > 0 ? { errorCostUsd: spend.errorCostUsd } : {}
|
|
80022
80216
|
});
|
|
80023
80217
|
break;
|
|
80024
80218
|
}
|
|
@@ -80031,7 +80225,7 @@ async function handlePipelineFailure(ctx, pipelineResult) {
|
|
|
80031
80225
|
});
|
|
80032
80226
|
prdDirty = true;
|
|
80033
80227
|
break;
|
|
80034
|
-
case "fail":
|
|
80228
|
+
case "fail": {
|
|
80035
80229
|
markStoryFailed(prd, ctx.story.id, pipelineResult.context.tddFailureCategory, pipelineResult.stoppedAtStage, ctx.statusWriter);
|
|
80036
80230
|
await savePRD(prd, ctx.prdPath);
|
|
80037
80231
|
prdDirty = true;
|
|
@@ -80046,6 +80240,7 @@ async function handlePipelineFailure(ctx, pipelineResult) {
|
|
|
80046
80240
|
if (ctx.featureDir) {
|
|
80047
80241
|
await appendProgress(ctx.featureDir, ctx.story.id, "failed", `${ctx.story.title} \u2014 ${pipelineResult.reason}`);
|
|
80048
80242
|
}
|
|
80243
|
+
const spend = storySpendUsd(ctx.runtime.costAggregator, ctx.story.id, ctx.totalCost);
|
|
80049
80244
|
pipelineEventBus.emit({
|
|
80050
80245
|
type: "story:failed",
|
|
80051
80246
|
storyId: ctx.story.id,
|
|
@@ -80054,7 +80249,8 @@ async function handlePipelineFailure(ctx, pipelineResult) {
|
|
|
80054
80249
|
countsTowardEscalation: true,
|
|
80055
80250
|
feature: ctx.feature,
|
|
80056
80251
|
attempts: ctx.story.attempts,
|
|
80057
|
-
cost:
|
|
80252
|
+
cost: spend.cost,
|
|
80253
|
+
...spend.errorCostUsd > 0 ? { errorCostUsd: spend.errorCostUsd } : {}
|
|
80058
80254
|
});
|
|
80059
80255
|
if (ctx.story.attempts !== undefined && ctx.story.attempts >= ctx.config.execution.rectification.maxAttemptsTotal) {
|
|
80060
80256
|
await pipelineEventBus.emitAsync({
|
|
@@ -80066,6 +80262,7 @@ async function handlePipelineFailure(ctx, pipelineResult) {
|
|
|
80066
80262
|
});
|
|
80067
80263
|
}
|
|
80068
80264
|
break;
|
|
80265
|
+
}
|
|
80069
80266
|
case "escalate": {
|
|
80070
80267
|
const runtimeCrashResult = pipelineResult.context.tddFailureCategory === "runtime-crash" ? { status: "RUNTIME_CRASH", success: false } : undefined;
|
|
80071
80268
|
const escalationResult = await _resultHandlerDeps.handleTierEscalation({
|
|
@@ -80106,6 +80303,7 @@ var init_pipeline_result_handler = __esm(() => {
|
|
|
80106
80303
|
init_pipeline2();
|
|
80107
80304
|
init_logger2();
|
|
80108
80305
|
init_prd();
|
|
80306
|
+
init_runtime2();
|
|
80109
80307
|
init_bun_deps();
|
|
80110
80308
|
init_git();
|
|
80111
80309
|
init_worktree();
|
|
@@ -81127,7 +81325,7 @@ function isExecutionFailure(story) {
|
|
|
81127
81325
|
return story != null && (story.status === "failed" || story.status === "regression-failed");
|
|
81128
81326
|
}
|
|
81129
81327
|
function synthesizeBackfillMetric(args) {
|
|
81130
|
-
const { storyId, story, totalCostUsd, config: config2, defaultAgent, timestamp, fallbackHops, runtimeCrashes } = args;
|
|
81328
|
+
const { storyId, story, totalCostUsd, errorCostUsd, config: config2, defaultAgent, timestamp, fallbackHops, runtimeCrashes } = args;
|
|
81131
81329
|
if (story != null && isExecutionFailure(story)) {
|
|
81132
81330
|
const tier = story.routing?.modelTier ?? "balanced";
|
|
81133
81331
|
const agent = story.routing?.agent ?? defaultAgent;
|
|
@@ -81149,6 +81347,7 @@ function synthesizeBackfillMetric(args) {
|
|
|
81149
81347
|
finalTier,
|
|
81150
81348
|
success: false,
|
|
81151
81349
|
cost: totalCostUsd,
|
|
81350
|
+
...errorCostUsd > 0 ? { errorCostUsd } : {},
|
|
81152
81351
|
durationMs: 0,
|
|
81153
81352
|
firstPassSuccess: false,
|
|
81154
81353
|
startedAt: timestamp,
|
|
@@ -81167,6 +81366,7 @@ function synthesizeBackfillMetric(args) {
|
|
|
81167
81366
|
finalTier: "balanced",
|
|
81168
81367
|
success: story?.passes ?? false,
|
|
81169
81368
|
cost: totalCostUsd,
|
|
81369
|
+
...errorCostUsd > 0 ? { errorCostUsd } : {},
|
|
81170
81370
|
durationMs: 0,
|
|
81171
81371
|
firstPassSuccess: story?.passes ?? false,
|
|
81172
81372
|
startedAt: timestamp,
|
|
@@ -81210,7 +81410,9 @@ function applyBackfill(input) {
|
|
|
81210
81410
|
stories
|
|
81211
81411
|
});
|
|
81212
81412
|
for (const storyId of domain2) {
|
|
81213
|
-
const
|
|
81413
|
+
const snap = aggByStory[storyId];
|
|
81414
|
+
const errorCostUsd = snap?.totalErrorCostUsd ?? 0;
|
|
81415
|
+
const totalCostUsd = (snap?.totalCostUsd ?? 0) + errorCostUsd;
|
|
81214
81416
|
const fallbackHops = toFallbackHops(agentFallbacks.get(storyId), storyId);
|
|
81215
81417
|
const runtimeCrashes = runtimeCrashRetries.get(storyId) ?? 0;
|
|
81216
81418
|
const story = storyById.get(storyId);
|
|
@@ -81223,6 +81425,7 @@ function applyBackfill(input) {
|
|
|
81223
81425
|
storyId,
|
|
81224
81426
|
story,
|
|
81225
81427
|
totalCostUsd,
|
|
81428
|
+
errorCostUsd,
|
|
81226
81429
|
config: config2,
|
|
81227
81430
|
defaultAgent,
|
|
81228
81431
|
timestamp,
|
|
@@ -81233,7 +81436,11 @@ function applyBackfill(input) {
|
|
|
81233
81436
|
}
|
|
81234
81437
|
const existing = allStoryMetrics[existingIdx];
|
|
81235
81438
|
if (totalCostUsd > (existing.cost ?? 0)) {
|
|
81236
|
-
allStoryMetrics[existingIdx] = {
|
|
81439
|
+
allStoryMetrics[existingIdx] = {
|
|
81440
|
+
...existing,
|
|
81441
|
+
cost: totalCostUsd,
|
|
81442
|
+
...errorCostUsd > 0 ? { errorCostUsd } : {}
|
|
81443
|
+
};
|
|
81237
81444
|
}
|
|
81238
81445
|
}
|
|
81239
81446
|
}
|
|
@@ -82167,7 +82374,9 @@ async function handleRunCompletion(options) {
|
|
|
82167
82374
|
});
|
|
82168
82375
|
}
|
|
82169
82376
|
const aggSnap = options.runtime.costAggregator.snapshot();
|
|
82170
|
-
const reportedTotal = aggSnap
|
|
82377
|
+
const reportedTotal = totalSpendUsd(aggSnap);
|
|
82378
|
+
const errorCostUsd = aggSnap.totalErrorCostUsd;
|
|
82379
|
+
const errorCostField = errorCostUsd > 0 ? { errorCostUsd } : {};
|
|
82171
82380
|
const aggByStage = options.runtime.costAggregator.byStage();
|
|
82172
82381
|
const aggByStory = options.runtime.costAggregator.byStory();
|
|
82173
82382
|
applyBackfill({
|
|
@@ -82200,6 +82409,7 @@ async function handleRunCompletion(options) {
|
|
|
82200
82409
|
pausedStories: finalCounts.paused,
|
|
82201
82410
|
durationMs,
|
|
82202
82411
|
totalCost: reportedTotal,
|
|
82412
|
+
...errorCostField,
|
|
82203
82413
|
...fallbackAggregate && { fallback: fallbackAggregate }
|
|
82204
82414
|
});
|
|
82205
82415
|
await pipelineEventBus.drain();
|
|
@@ -82209,6 +82419,7 @@ async function handleRunCompletion(options) {
|
|
|
82209
82419
|
startedAt,
|
|
82210
82420
|
completedAt: runCompletedAt,
|
|
82211
82421
|
totalCost: reportedTotal,
|
|
82422
|
+
...errorCostField,
|
|
82212
82423
|
totalStories: allStoryMetrics.length,
|
|
82213
82424
|
storiesCompleted,
|
|
82214
82425
|
storiesFailed: finalCounts.failed,
|
|
@@ -82273,6 +82484,7 @@ async function handleRunCompletion(options) {
|
|
|
82273
82484
|
storiesFailed: finalCounts.failed,
|
|
82274
82485
|
storiesPending: finalCounts.pending,
|
|
82275
82486
|
totalCost: reportedTotal,
|
|
82487
|
+
...errorCostField,
|
|
82276
82488
|
...contextCostUsd > 0 && { contextCostUsd },
|
|
82277
82489
|
...Object.keys(aggByStage).length > 0 && { costByStage: aggByStage },
|
|
82278
82490
|
...Object.keys(aggByStory).length > 0 && { costByStory: aggByStory },
|
|
@@ -82307,6 +82519,7 @@ var init_run_completion = __esm(() => {
|
|
|
82307
82519
|
init_pipeline2();
|
|
82308
82520
|
init_prd();
|
|
82309
82521
|
init_project();
|
|
82522
|
+
init_runtime2();
|
|
82310
82523
|
init_session2();
|
|
82311
82524
|
init_detect();
|
|
82312
82525
|
init_verification();
|
|
@@ -88898,6 +89111,7 @@ var init_phase = __esm(() => {
|
|
|
88898
89111
|
init_forge();
|
|
88899
89112
|
init_logger2();
|
|
88900
89113
|
init_pipeline2();
|
|
89114
|
+
init_runtime2();
|
|
88901
89115
|
init_config3();
|
|
88902
89116
|
init_context3();
|
|
88903
89117
|
init_machine();
|
|
@@ -88911,7 +89125,7 @@ var init_phase = __esm(() => {
|
|
|
88911
89125
|
runFinishMachine,
|
|
88912
89126
|
sendTelegramNotify,
|
|
88913
89127
|
now: () => new Date().toISOString(),
|
|
88914
|
-
snapshotCost: (runtime) => runtime.costAggregator.snapshot()
|
|
89128
|
+
snapshotCost: (runtime) => totalSpendUsd(runtime.costAggregator.snapshot())
|
|
88915
89129
|
};
|
|
88916
89130
|
});
|
|
88917
89131
|
|
|
@@ -89592,6 +89806,7 @@ function wireReporters(bus, pluginRegistry, runId, startTime, projectKey) {
|
|
|
89592
89806
|
status: "completed",
|
|
89593
89807
|
runElapsedMs: ev.runElapsedMs,
|
|
89594
89808
|
cost: ev.cost ?? 0,
|
|
89809
|
+
...ev.errorCostUsd !== undefined ? { errorCostUsd: ev.errorCostUsd } : {},
|
|
89595
89810
|
tier: ev.modelTier ?? "balanced",
|
|
89596
89811
|
testStrategy: ev.testStrategy ?? "test-after"
|
|
89597
89812
|
});
|
|
@@ -89613,7 +89828,8 @@ function wireReporters(bus, pluginRegistry, runId, startTime, projectKey) {
|
|
|
89613
89828
|
storyId: ev.storyId,
|
|
89614
89829
|
status: "failed",
|
|
89615
89830
|
runElapsedMs: Date.now() - startTime,
|
|
89616
|
-
cost: 0,
|
|
89831
|
+
cost: ev.cost ?? 0,
|
|
89832
|
+
...ev.errorCostUsd !== undefined ? { errorCostUsd: ev.errorCostUsd } : {},
|
|
89617
89833
|
tier: "balanced",
|
|
89618
89834
|
testStrategy: "test-after"
|
|
89619
89835
|
});
|
|
@@ -89635,7 +89851,8 @@ function wireReporters(bus, pluginRegistry, runId, startTime, projectKey) {
|
|
|
89635
89851
|
storyId: ev.storyId,
|
|
89636
89852
|
status: "paused",
|
|
89637
89853
|
runElapsedMs: Date.now() - startTime,
|
|
89638
|
-
cost: 0,
|
|
89854
|
+
cost: ev.cost ?? 0,
|
|
89855
|
+
...ev.errorCostUsd !== undefined ? { errorCostUsd: ev.errorCostUsd } : {},
|
|
89639
89856
|
tier: "balanced",
|
|
89640
89857
|
testStrategy: "test-after"
|
|
89641
89858
|
});
|
|
@@ -89675,6 +89892,7 @@ function wireReporters(bus, pluginRegistry, runId, startTime, projectKey) {
|
|
|
89675
89892
|
runId,
|
|
89676
89893
|
totalDurationMs: Date.now() - startTime,
|
|
89677
89894
|
totalCost: ev.totalCost ?? 0,
|
|
89895
|
+
...ev.errorCostUsd !== undefined ? { errorCostUsd: ev.errorCostUsd } : {},
|
|
89678
89896
|
storySummary: {
|
|
89679
89897
|
completed: ev.passedStories,
|
|
89680
89898
|
failed: ev.failedStories,
|
|
@@ -89698,6 +89916,30 @@ var init_reporters = __esm(() => {
|
|
|
89698
89916
|
init_logger2();
|
|
89699
89917
|
});
|
|
89700
89918
|
|
|
89919
|
+
// src/execution/cost-guard.ts
|
|
89920
|
+
async function enforceCostLimit(ctx, totalCost2, costLimit, storyId) {
|
|
89921
|
+
const enforcedCost = Math.max(totalCost2, totalSpendUsd(ctx.runtime.costAggregator.snapshot()));
|
|
89922
|
+
if (enforcedCost < costLimit)
|
|
89923
|
+
return { stop: false, enforcedCost };
|
|
89924
|
+
const shouldProceed = ctx.interactionChain && isTriggerEnabled("cost-exceeded", ctx.config) ? await checkCostExceeded({ featureName: ctx.feature, cost: enforcedCost, limit: costLimit }, ctx.config, ctx.interactionChain) : false;
|
|
89925
|
+
if (!shouldProceed) {
|
|
89926
|
+
pipelineEventBus.emit({
|
|
89927
|
+
type: "run:paused",
|
|
89928
|
+
reason: `Cost limit reached: $${enforcedCost.toFixed(2)}`,
|
|
89929
|
+
...storyId !== undefined ? { storyId } : {},
|
|
89930
|
+
cost: enforcedCost
|
|
89931
|
+
});
|
|
89932
|
+
return { stop: true, enforcedCost };
|
|
89933
|
+
}
|
|
89934
|
+
pipelineEventBus.emit({ type: "run:resumed", feature: ctx.feature });
|
|
89935
|
+
return { stop: false, enforcedCost };
|
|
89936
|
+
}
|
|
89937
|
+
var init_cost_guard = __esm(() => {
|
|
89938
|
+
init_runtime2();
|
|
89939
|
+
init_triggers();
|
|
89940
|
+
init_event_bus();
|
|
89941
|
+
});
|
|
89942
|
+
|
|
89701
89943
|
// src/execution/cost-warning.ts
|
|
89702
89944
|
async function maybeSendCostWarning(ctx, enforcedCost, costLimit, warningSent) {
|
|
89703
89945
|
if (warningSent || !ctx.interactionChain || !isTriggerEnabled("cost-warning", ctx.config))
|
|
@@ -90231,23 +90473,6 @@ async function closeStoryIfTerminal(ctx, storyId, iter) {
|
|
|
90231
90473
|
await closeStorySessions(ctx.sessionManager, storyId, ctx.agentGetFn);
|
|
90232
90474
|
ctx.agentManager?.resetTransientUnavailable?.();
|
|
90233
90475
|
}
|
|
90234
|
-
async function enforceCostLimit(ctx, totalCost2, costLimit, storyId) {
|
|
90235
|
-
const enforcedCost = Math.max(totalCost2, ctx.runtime.costAggregator.snapshot().totalCostUsd);
|
|
90236
|
-
if (enforcedCost < costLimit)
|
|
90237
|
-
return { stop: false, enforcedCost };
|
|
90238
|
-
const shouldProceed = ctx.interactionChain && isTriggerEnabled("cost-exceeded", ctx.config) ? await checkCostExceeded({ featureName: ctx.feature, cost: enforcedCost, limit: costLimit }, ctx.config, ctx.interactionChain) : false;
|
|
90239
|
-
if (!shouldProceed) {
|
|
90240
|
-
pipelineEventBus.emit({
|
|
90241
|
-
type: "run:paused",
|
|
90242
|
-
reason: `Cost limit reached: $${enforcedCost.toFixed(2)}`,
|
|
90243
|
-
...storyId !== undefined ? { storyId } : {},
|
|
90244
|
-
cost: enforcedCost
|
|
90245
|
-
});
|
|
90246
|
-
return { stop: true, enforcedCost };
|
|
90247
|
-
}
|
|
90248
|
-
pipelineEventBus.emit({ type: "run:resumed", feature: ctx.feature });
|
|
90249
|
-
return { stop: false, enforcedCost };
|
|
90250
|
-
}
|
|
90251
90476
|
async function executeUnified(ctx, initialPrd) {
|
|
90252
90477
|
const logger = getSafeLogger();
|
|
90253
90478
|
let prd = initialPrd;
|
|
@@ -90666,7 +90891,7 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
90666
90891
|
iter.prdDirty
|
|
90667
90892
|
];
|
|
90668
90893
|
await closeStoryIfTerminal(ctx, selection.story.id, iter);
|
|
90669
|
-
warningSent = await maybeSendCostWarning(ctx, Math.max(totalCost2, ctx.runtime.costAggregator.snapshot()
|
|
90894
|
+
warningSent = await maybeSendCostWarning(ctx, Math.max(totalCost2, totalSpendUsd(ctx.runtime.costAggregator.snapshot())), costLimit, warningSent);
|
|
90670
90895
|
if (iter.prdDirty) {
|
|
90671
90896
|
prd = await loadPRD(ctx.prdPath);
|
|
90672
90897
|
prdDirty = false;
|
|
@@ -90740,6 +90965,7 @@ function reconcileBatchOutcome(prd, batchResult) {
|
|
|
90740
90965
|
var TERMINAL_ACTIONS, _prevRunUnsubscribers, _unifiedExecutorDeps;
|
|
90741
90966
|
var init_unified_executor = __esm(() => {
|
|
90742
90967
|
init_pipeline2();
|
|
90968
|
+
init_runtime2();
|
|
90743
90969
|
init_triggers();
|
|
90744
90970
|
init_logger2();
|
|
90745
90971
|
init_metrics();
|
|
@@ -90754,6 +90980,7 @@ var init_unified_executor = __esm(() => {
|
|
|
90754
90980
|
init_routing();
|
|
90755
90981
|
init_bun_deps();
|
|
90756
90982
|
init_path_filters();
|
|
90983
|
+
init_cost_guard();
|
|
90757
90984
|
init_cost_warning();
|
|
90758
90985
|
init_crash_recovery();
|
|
90759
90986
|
init_deferred_review();
|
|
@@ -91442,6 +91669,7 @@ var init_completion = __esm(() => {
|
|
|
91442
91669
|
init_logger2();
|
|
91443
91670
|
init_metrics();
|
|
91444
91671
|
init_prd();
|
|
91672
|
+
init_runtime2();
|
|
91445
91673
|
init_git();
|
|
91446
91674
|
init_verification();
|
|
91447
91675
|
init_event_bus();
|
|
@@ -91452,7 +91680,8 @@ var init_completion = __esm(() => {
|
|
|
91452
91680
|
async execute(ctx) {
|
|
91453
91681
|
const logger = getLogger();
|
|
91454
91682
|
const isBatch = ctx.stories.length > 1;
|
|
91455
|
-
const
|
|
91683
|
+
const sessionSpend = storySpendUsd(ctx.runtime.costAggregator, ctx.story.id, 0);
|
|
91684
|
+
const sessionCost = sessionSpend.cost;
|
|
91456
91685
|
const persistPrd2 = ctx.skipPrdPersistence !== true;
|
|
91457
91686
|
const prdPath = ctx.prdPath ?? join104(ctx.featureDir ?? featureDir(ctx.workdir, "unknown"), "prd.json");
|
|
91458
91687
|
const storyStartTime = ctx.storyStartTime || new Date().toISOString();
|
|
@@ -91497,6 +91726,7 @@ var init_completion = __esm(() => {
|
|
|
91497
91726
|
markStoryPassed(ctx.prd, completedStory.id);
|
|
91498
91727
|
}
|
|
91499
91728
|
const costPerStory = sessionCost / ctx.stories.length;
|
|
91729
|
+
const errorCostPerStory = sessionSpend.errorCostUsd / ctx.stories.length;
|
|
91500
91730
|
logger.info("completion", "Story passed", {
|
|
91501
91731
|
storyId: completedStory.id,
|
|
91502
91732
|
cost: costPerStory
|
|
@@ -91518,6 +91748,7 @@ var init_completion = __esm(() => {
|
|
|
91518
91748
|
passed: true,
|
|
91519
91749
|
runElapsedMs: storyMetric?.durationMs ?? 0,
|
|
91520
91750
|
cost: costPerStory,
|
|
91751
|
+
...errorCostPerStory > 0 ? { errorCostUsd: errorCostPerStory } : {},
|
|
91521
91752
|
modelTier: ctx.routing?.modelTier,
|
|
91522
91753
|
testStrategy: ctx.routing?.testStrategy
|
|
91523
91754
|
});
|
|
@@ -93886,6 +94117,7 @@ async function displayLastRunMetrics(workdir) {
|
|
|
93886
94117
|
storiesCompleted: lastRun.storiesCompleted,
|
|
93887
94118
|
storiesFailed: lastRun.storiesFailed,
|
|
93888
94119
|
totalCost: lastRun.totalCost,
|
|
94120
|
+
...lastRun.errorCostUsd !== undefined ? { errorCostUsd: lastRun.errorCostUsd } : {},
|
|
93889
94121
|
avgCostPerStory: reportedLastRun?.avgCostPerStory ?? 0
|
|
93890
94122
|
});
|
|
93891
94123
|
const sortedStories = [...lastRun.stories].sort((a, b) => b.cost - a.cost);
|