@tradejs/cli 3.1.29-beta.282 → 3.1.29-beta.284
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +8201 -4165
- package/dist/lib/aiPocketSearch.js +3 -0
- package/dist/lib/aiTrainResearch.js +7 -0
- package/dist/lib/coreResearch.js +111 -36
- package/dist/lib/mcpReplayProcess.js +1 -1
- package/dist/lib/runtimeFeedbackProcess.js +1700 -0
- package/dist/lib/strategyResearch.js +6483 -0
- package/dist/scripts/aiPocketSearch.js +3 -0
- package/dist/scripts/coreResearch.js +113 -38
- package/dist/scripts/mcpWorker.js +57 -9
- package/dist/scripts/replay.js +173 -47
- package/dist/scripts/replayRunner.js +173 -47
- package/dist/scripts/replayRuntimeEvidence.js +109 -6
- package/dist/scripts/runtimeFeedbackProcess.js +1736 -0
- package/dist/scripts/strategyResearch.js +6904 -0
- package/package.json +8 -7
|
@@ -597,6 +597,7 @@ var DAYS_PER_WEEK = 7;
|
|
|
597
597
|
var DAYS_PER_MONTH = 30.4375;
|
|
598
598
|
var PRODUCTION_CANDIDATE_MIN_EVENTS = 25;
|
|
599
599
|
var OUTCOME_SEGMENTS = /* @__PURE__ */ new Set([
|
|
600
|
+
"researchentrydecision",
|
|
600
601
|
"actual",
|
|
601
602
|
"aiapproved",
|
|
602
603
|
"approvalallowednow",
|
|
@@ -881,6 +882,8 @@ var classifyAiPocketFeaturePath = (path3) => {
|
|
|
881
882
|
if (METADATA_LEAVES.has(leaf) || leaf.endsWith("fingerprint") || normalized.some((segment) => segment === "metadata")) {
|
|
882
883
|
return "metadata";
|
|
883
884
|
}
|
|
885
|
+
if (normalized.some((segment) => OUTCOME_SEGMENTS.has(segment)))
|
|
886
|
+
return "derived-policy";
|
|
884
887
|
if (DERIVED_POLICY_FRAGMENTS.some(
|
|
885
888
|
(fragment) => !(fragment === "gatefeature" && isStrategySetupEvidence) && normalizedPath.includes(fragment)
|
|
886
889
|
)) {
|
|
@@ -33,7 +33,7 @@ __export(coreResearch_exports, {
|
|
|
33
33
|
main: () => main
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(coreResearch_exports);
|
|
36
|
-
var
|
|
36
|
+
var import_promises4 = __toESM(require("fs/promises"));
|
|
37
37
|
var import_node_path7 = __toESM(require("path"));
|
|
38
38
|
var import_chalk = __toESM(require("chalk"));
|
|
39
39
|
|
|
@@ -644,7 +644,7 @@ var writeCoreResearchStageIndex = async (rootDir) => {
|
|
|
644
644
|
};
|
|
645
645
|
|
|
646
646
|
// src/lib/coreResearch/orchestrator.ts
|
|
647
|
-
var
|
|
647
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
648
648
|
var import_node_path6 = __toESM(require("path"));
|
|
649
649
|
var import_node_child_process = require("child_process");
|
|
650
650
|
var import_node_crypto5 = require("crypto");
|
|
@@ -1010,6 +1010,8 @@ var calendarClusterBootstrap = (params) => {
|
|
|
1010
1010
|
const alpha = 1 - params.confidenceLevel;
|
|
1011
1011
|
const positive = samples.filter((value) => value > 0).length;
|
|
1012
1012
|
const probabilityPositive = positive / samples.length;
|
|
1013
|
+
const observed = deltas.reduce((sum, value) => sum + value, 0) / deltas.length;
|
|
1014
|
+
const oneSidedPValue = (1 + samples.filter((value) => value - observed >= observed - 1e-12).length) / (samples.length + 1);
|
|
1013
1015
|
return {
|
|
1014
1016
|
method: "calendar-cluster-bootstrap",
|
|
1015
1017
|
clusterDays: params.clusterDays,
|
|
@@ -1021,7 +1023,7 @@ var calendarClusterBootstrap = (params) => {
|
|
|
1021
1023
|
percentile(samples, 1 - alpha / 2)
|
|
1022
1024
|
],
|
|
1023
1025
|
probabilityPositive,
|
|
1024
|
-
oneSidedPValue
|
|
1026
|
+
oneSidedPValue,
|
|
1025
1027
|
holmAdjustedPValue: null
|
|
1026
1028
|
};
|
|
1027
1029
|
};
|
|
@@ -1088,7 +1090,7 @@ var erf = (value) => {
|
|
|
1088
1090
|
return sign * y;
|
|
1089
1091
|
};
|
|
1090
1092
|
var normalCdf = (value) => 0.5 * (1 + erf(value / Math.SQRT2));
|
|
1091
|
-
var deflatedSharpeDiagnostic = (returns, trials) => {
|
|
1093
|
+
var deflatedSharpeDiagnostic = (returns, trials, trialSharpes) => {
|
|
1092
1094
|
const periods = returns.length;
|
|
1093
1095
|
if (periods < 3) {
|
|
1094
1096
|
return {
|
|
@@ -1113,9 +1115,18 @@ var deflatedSharpeDiagnostic = (returns, trials) => {
|
|
|
1113
1115
|
const observedSharpe = mean / standardDeviation;
|
|
1114
1116
|
const skew = centered.reduce((sum, value) => sum + (value / standardDeviation) ** 3, 0) / periods;
|
|
1115
1117
|
const kurtosis = centered.reduce((sum, value) => sum + (value / standardDeviation) ** 4, 0) / periods;
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1118
|
-
|
|
1118
|
+
const available = trialSharpes?.filter(Number.isFinite) ?? [];
|
|
1119
|
+
if (trials > 1 && available.length < 2)
|
|
1120
|
+
return {
|
|
1121
|
+
periods,
|
|
1122
|
+
observedSharpe,
|
|
1123
|
+
expectedMaximumSharpe: null,
|
|
1124
|
+
probabilityAboveSelectionBias: null
|
|
1125
|
+
};
|
|
1126
|
+
const trialMean = available.length ? available.reduce((sum, value) => sum + value, 0) / available.length : 0;
|
|
1127
|
+
const trialVariance = available.length > 1 ? available.reduce((sum, value) => sum + (value - trialMean) ** 2, 0) / (available.length - 1) : 0;
|
|
1128
|
+
const gamma = 0.5772156649015329;
|
|
1129
|
+
const expectedMaximumSharpe = trials <= 1 ? 0 : Math.sqrt(trialVariance) * ((1 - gamma) * inverseNormalCdf(1 - 1 / trials) + gamma * inverseNormalCdf(1 - 1 / (trials * Math.E)));
|
|
1119
1130
|
const standardError = Math.sqrt(
|
|
1120
1131
|
Math.max(
|
|
1121
1132
|
1e-12,
|
|
@@ -1133,6 +1144,27 @@ var deflatedSharpeDiagnostic = (returns, trials) => {
|
|
|
1133
1144
|
};
|
|
1134
1145
|
var combinations = (items, choose) => {
|
|
1135
1146
|
const output = [];
|
|
1147
|
+
if (items.length > 10) {
|
|
1148
|
+
const random = createSeededRandom(`cscv-balanced-${items.length}`);
|
|
1149
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1150
|
+
for (let attempt = 0; output.length < 512 && attempt < 1e5; attempt++) {
|
|
1151
|
+
const shuffled = [...items];
|
|
1152
|
+
for (let index = shuffled.length - 1; index > 0; index--) {
|
|
1153
|
+
const swap = Math.floor(random() * (index + 1));
|
|
1154
|
+
[shuffled[index], shuffled[swap]] = [shuffled[swap], shuffled[index]];
|
|
1155
|
+
}
|
|
1156
|
+
const selected = shuffled.slice(0, choose).sort((a, b) => a - b);
|
|
1157
|
+
const complement = items.filter((item) => !selected.includes(item));
|
|
1158
|
+
for (const half of [selected, complement]) {
|
|
1159
|
+
const key = half.join(",");
|
|
1160
|
+
if (!seen.has(key)) {
|
|
1161
|
+
seen.add(key);
|
|
1162
|
+
output.push(half);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
return output;
|
|
1167
|
+
}
|
|
1136
1168
|
const visit = (start, selected) => {
|
|
1137
1169
|
if (output.length >= 512) return;
|
|
1138
1170
|
if (selected.length === choose) {
|
|
@@ -1171,14 +1203,23 @@ var probabilityOfBacktestOverfitting = (performanceByVariant) => {
|
|
|
1171
1203
|
const inScores = performanceByVariant.map(
|
|
1172
1204
|
(performance) => inSample.reduce((sum, index) => sum + performance[index], 0)
|
|
1173
1205
|
);
|
|
1174
|
-
const
|
|
1206
|
+
const maximum = Math.max(...inScores);
|
|
1207
|
+
const selected = inScores.flatMap(
|
|
1208
|
+
(score, index) => score === maximum ? [index] : []
|
|
1209
|
+
);
|
|
1175
1210
|
const outScores = performanceByVariant.map(
|
|
1176
1211
|
(performance) => outSample.reduce((sum, index) => sum + performance[index], 0)
|
|
1177
1212
|
);
|
|
1178
|
-
const
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1213
|
+
for (const winner of selected) {
|
|
1214
|
+
const less = outScores.filter(
|
|
1215
|
+
(score) => score < outScores[winner]
|
|
1216
|
+
).length;
|
|
1217
|
+
const equal = outScores.filter(
|
|
1218
|
+
(score) => score === outScores[winner]
|
|
1219
|
+
).length;
|
|
1220
|
+
const omega = (less + (equal + 1) / 2) / (outScores.length + 1);
|
|
1221
|
+
if (omega < 0.5) belowMedian += 1 / selected.length;
|
|
1222
|
+
}
|
|
1182
1223
|
}
|
|
1183
1224
|
return {
|
|
1184
1225
|
method: "CSCV",
|
|
@@ -2108,34 +2149,55 @@ var applyCoreResearchRobustnessGuardrails = (params) => {
|
|
|
2108
2149
|
};
|
|
2109
2150
|
|
|
2110
2151
|
// src/lib/coreResearch/trace.ts
|
|
2111
|
-
var
|
|
2152
|
+
var import_node_fs4 = require("fs");
|
|
2153
|
+
var import_node_readline2 = __toESM(require("readline"));
|
|
2112
2154
|
var import_node_path4 = __toESM(require("path"));
|
|
2113
2155
|
var summarizeCoreResearchTrace = async (filePaths = []) => {
|
|
2114
2156
|
const events = /* @__PURE__ */ Object.create(null);
|
|
2115
2157
|
const skipCounts = /* @__PURE__ */ Object.create(null);
|
|
2116
2158
|
const exitCodes = /* @__PURE__ */ Object.create(null);
|
|
2159
|
+
const policyRejections = {};
|
|
2160
|
+
const policyExamples = [];
|
|
2117
2161
|
for (const inputPath of filePaths) {
|
|
2118
2162
|
const filePath = import_node_path4.default.resolve(inputPath);
|
|
2119
|
-
const
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2163
|
+
const input = (0, import_node_fs4.createReadStream)(filePath);
|
|
2164
|
+
const lines = import_node_readline2.default.createInterface({ input, crlfDelay: Infinity });
|
|
2165
|
+
let index = -1;
|
|
2166
|
+
try {
|
|
2167
|
+
for await (const line of lines) {
|
|
2168
|
+
index++;
|
|
2169
|
+
if (!line.trim()) continue;
|
|
2170
|
+
let event;
|
|
2171
|
+
try {
|
|
2172
|
+
event = JSON.parse(line);
|
|
2173
|
+
} catch (error) {
|
|
2174
|
+
throw new Error(
|
|
2175
|
+
`${filePath}:${index + 1} contains invalid trace JSON: ${String(error)}`
|
|
2176
|
+
);
|
|
2177
|
+
}
|
|
2178
|
+
events[event.event] = (events[event.event] ?? 0) + 1;
|
|
2179
|
+
if (event.event === "skip_summary") {
|
|
2180
|
+
for (const [reason, count] of Object.entries(event.skipCounts)) {
|
|
2181
|
+
skipCounts[reason] = (skipCounts[reason] ?? 0) + count;
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
if (event.event === "position_exited" && event.exitCode) {
|
|
2185
|
+
exitCodes[event.exitCode] = (exitCodes[event.exitCode] ?? 0) + 1;
|
|
2186
|
+
}
|
|
2187
|
+
if ("researchEntryDecision" in event && event.researchEntryDecision && !event.researchEntryDecision.allowed) {
|
|
2188
|
+
const reason = event.researchEntryDecision.reason;
|
|
2189
|
+
policyRejections[reason] = (policyRejections[reason] ?? 0) + 1;
|
|
2190
|
+
if (policyExamples.length < 20)
|
|
2191
|
+
policyExamples.push({
|
|
2192
|
+
timestamp: event.timestamp,
|
|
2193
|
+
symbol: event.symbol,
|
|
2194
|
+
decision: event.researchEntryDecision
|
|
2195
|
+
});
|
|
2134
2196
|
}
|
|
2135
2197
|
}
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2198
|
+
} finally {
|
|
2199
|
+
lines.close();
|
|
2200
|
+
input.destroy();
|
|
2139
2201
|
}
|
|
2140
2202
|
}
|
|
2141
2203
|
return {
|
|
@@ -2149,7 +2211,13 @@ var summarizeCoreResearchTrace = async (filePaths = []) => {
|
|
|
2149
2211
|
Object.entries(exitCodes).sort(
|
|
2150
2212
|
([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
|
|
2151
2213
|
)
|
|
2152
|
-
)
|
|
2214
|
+
),
|
|
2215
|
+
...policyExamples.length ? {
|
|
2216
|
+
researchPolicy: {
|
|
2217
|
+
rejections: policyRejections,
|
|
2218
|
+
examples: policyExamples
|
|
2219
|
+
}
|
|
2220
|
+
} : {}
|
|
2153
2221
|
};
|
|
2154
2222
|
};
|
|
2155
2223
|
|
|
@@ -2436,7 +2504,14 @@ var analyzeCoreResearchPrepared = async (spec, options = {}) => {
|
|
|
2436
2504
|
analysis.variant.id,
|
|
2437
2505
|
deflatedSharpeDiagnostic(
|
|
2438
2506
|
analysis.monthly.map((window) => window.cohorts.ALL.pnl),
|
|
2439
|
-
Math.max(comparisons.length, familyHypotheses)
|
|
2507
|
+
Math.max(comparisons.length, familyHypotheses),
|
|
2508
|
+
analyses.flatMap((entry) => {
|
|
2509
|
+
const diagnostic = deflatedSharpeDiagnostic(
|
|
2510
|
+
entry.monthly.map((window) => window.cohorts.ALL.pnl),
|
|
2511
|
+
1
|
|
2512
|
+
);
|
|
2513
|
+
return diagnostic.observedSharpe == null ? [] : [diagnostic.observedSharpe];
|
|
2514
|
+
})
|
|
2440
2515
|
)
|
|
2441
2516
|
])
|
|
2442
2517
|
),
|
|
@@ -2461,7 +2536,7 @@ var analyzeCoreResearchPrepared = async (spec, options = {}) => {
|
|
|
2461
2536
|
const tradesByVariant = new Map(
|
|
2462
2537
|
windowed.map((entry) => [entry.variant.id, entry.trades])
|
|
2463
2538
|
);
|
|
2464
|
-
await
|
|
2539
|
+
await import_promises3.default.writeFile(
|
|
2465
2540
|
paths.reportPath,
|
|
2466
2541
|
buildCoreResearchHtml({ spec, result, tradesByVariant }),
|
|
2467
2542
|
"utf8"
|
|
@@ -2481,7 +2556,7 @@ var analyzeCoreResearchPrepared = async (spec, options = {}) => {
|
|
|
2481
2556
|
if (error.code !== "ENOENT") throw error;
|
|
2482
2557
|
}
|
|
2483
2558
|
try {
|
|
2484
|
-
for (const name of await
|
|
2559
|
+
for (const name of await import_promises3.default.readdir(paths.runLogDir)) {
|
|
2485
2560
|
if (!name.endsWith(".log")) continue;
|
|
2486
2561
|
artifactHashes[`runs/${name}`] = await sha256File(
|
|
2487
2562
|
import_node_path6.default.join(paths.runLogDir, name)
|
|
@@ -2681,7 +2756,7 @@ var createCoreResearchSpecTemplate = (params) => {
|
|
|
2681
2756
|
};
|
|
2682
2757
|
var runCommand = async (params) => {
|
|
2683
2758
|
if (!params.command.length) throw new Error("Cannot run an empty command");
|
|
2684
|
-
await
|
|
2759
|
+
await import_promises3.default.mkdir(import_node_path6.default.dirname(params.logPath), { recursive: true });
|
|
2685
2760
|
const chunks = [];
|
|
2686
2761
|
const child = (0, import_node_child_process.spawn)(params.command[0], params.command.slice(1), {
|
|
2687
2762
|
cwd: params.cwd,
|
|
@@ -2701,7 +2776,7 @@ var runCommand = async (params) => {
|
|
|
2701
2776
|
child.once("close", (code) => resolve(code ?? 1));
|
|
2702
2777
|
});
|
|
2703
2778
|
const output = chunks.join("");
|
|
2704
|
-
await
|
|
2779
|
+
await import_promises3.default.writeFile(params.logPath, output, "utf8");
|
|
2705
2780
|
if (exitCode !== 0) {
|
|
2706
2781
|
throw new Error(
|
|
2707
2782
|
`Command failed with exit ${exitCode}: ${params.command.join(" ")}`
|
|
@@ -2919,7 +2994,7 @@ var readSymbols = async (argv, projectRoot) => {
|
|
|
2919
2994
|
const symbolsFile = valueAfter(argv, "--symbolsFile");
|
|
2920
2995
|
if (symbolsFile) {
|
|
2921
2996
|
const parsed = JSON.parse(
|
|
2922
|
-
await
|
|
2997
|
+
await import_promises4.default.readFile(import_node_path7.default.resolve(projectRoot, symbolsFile), "utf8")
|
|
2923
2998
|
);
|
|
2924
2999
|
if (!Array.isArray(parsed) || parsed.some((symbol) => typeof symbol !== "string")) {
|
|
2925
3000
|
throw new Error("--symbolsFile must contain a JSON string array");
|
|
@@ -103,7 +103,7 @@ var buildMcpReplayProcess = async ({
|
|
|
103
103
|
const heartbeat = JSON.parse(
|
|
104
104
|
await import_promises.default.readFile(import_node_path.default.join(root, "heartbeat.json"), "utf8")
|
|
105
105
|
);
|
|
106
|
-
if (!Number.isFinite(heartbeat.observedAt) || Date.now() - heartbeat.observedAt < 0 || Date.now() - heartbeat.observedAt > 12e4)
|
|
106
|
+
if (heartbeat.protocolVersion !== 2 || !Number.isFinite(heartbeat.observedAt) || Date.now() - heartbeat.observedAt < 0 || Date.now() - heartbeat.observedAt > 12e4)
|
|
107
107
|
throw new Error("Host replay runner heartbeat is stale");
|
|
108
108
|
const bytes = await import_promises.default.readFile(evidencePath);
|
|
109
109
|
const request = JSON.stringify({
|
|
@@ -136,14 +136,30 @@ var main = async (options = {}) => {
|
|
|
136
136
|
let leaseLost = false;
|
|
137
137
|
let manifestHash = "";
|
|
138
138
|
let replayReady = false;
|
|
139
|
+
let replayReadiness = "unknown";
|
|
140
|
+
let replayResources;
|
|
139
141
|
const updateReplayReady = async () => {
|
|
142
|
+
replayResources = void 0;
|
|
140
143
|
try {
|
|
141
144
|
const root = process.env.MCP_REPLAY_REQUEST_ROOT || `${process.env.PROJECT_CWD || process.cwd()}/data/mcp-replay-requests`;
|
|
142
145
|
const heartbeat2 = JSON.parse(
|
|
143
146
|
await (0, import_promises2.readFile)(`${root}/heartbeat.json`, "utf8")
|
|
144
147
|
);
|
|
145
|
-
replayReady = Number.isFinite(heartbeat2.observedAt) && Date.now() - heartbeat2.observedAt >= 0 && Date.now() - heartbeat2.observedAt < 12e4;
|
|
148
|
+
replayReady = heartbeat2.protocolVersion === 2 && Number.isFinite(heartbeat2.observedAt) && Date.now() - heartbeat2.observedAt >= 0 && Date.now() - heartbeat2.observedAt < 12e4;
|
|
149
|
+
replayReadiness = replayReady && (heartbeat2.readiness === "ready" || heartbeat2.readiness === "waiting_resources") ? heartbeat2.readiness : "unknown";
|
|
150
|
+
if (replayReady && Number.isSafeInteger(heartbeat2.memoryAvailableKb) && heartbeat2.memoryAvailableKb >= 0 && Number.isSafeInteger(heartbeat2.minimumAvailableKb) && heartbeat2.minimumAvailableKb > 0) {
|
|
151
|
+
replayResources = {
|
|
152
|
+
observedAt: heartbeat2.observedAt,
|
|
153
|
+
memoryAvailableKb: heartbeat2.memoryAvailableKb,
|
|
154
|
+
minimumAvailableKb: heartbeat2.minimumAvailableKb,
|
|
155
|
+
memoryDeficitKb: Math.max(
|
|
156
|
+
0,
|
|
157
|
+
heartbeat2.minimumAvailableKb - heartbeat2.memoryAvailableKb
|
|
158
|
+
)
|
|
159
|
+
};
|
|
160
|
+
}
|
|
146
161
|
} catch {
|
|
162
|
+
replayReadiness = "unknown";
|
|
147
163
|
replayReady = false;
|
|
148
164
|
}
|
|
149
165
|
};
|
|
@@ -160,7 +176,9 @@ var main = async (options = {}) => {
|
|
|
160
176
|
const heartbeatValue = () => ({
|
|
161
177
|
observedAt: Date.now(),
|
|
162
178
|
packageManifestSha256: manifestHash,
|
|
163
|
-
replayAvailable: replayReady
|
|
179
|
+
replayAvailable: replayReady,
|
|
180
|
+
replayReadiness,
|
|
181
|
+
replayResources
|
|
164
182
|
});
|
|
165
183
|
const stop = () => {
|
|
166
184
|
stopping = true;
|
|
@@ -221,7 +239,9 @@ var main = async (options = {}) => {
|
|
|
221
239
|
cursor = page.cursor;
|
|
222
240
|
for (const key of page.ids) {
|
|
223
241
|
const job = await import_redis.mcpStorage.get(key);
|
|
224
|
-
if (job
|
|
242
|
+
if (job && ["queued", "waiting_resources", "waiting_executor"].includes(
|
|
243
|
+
job.status
|
|
244
|
+
) && (!queued || job.createdAt < queued.createdAt))
|
|
225
245
|
queued = job;
|
|
226
246
|
}
|
|
227
247
|
} while (cursor !== "0" && !stopping);
|
|
@@ -232,13 +252,13 @@ var main = async (options = {}) => {
|
|
|
232
252
|
const jobKey = (0, import_mcp.mcpJobStorageId)(queued.userName, queued.id);
|
|
233
253
|
const started = {
|
|
234
254
|
...queued,
|
|
235
|
-
status: "running",
|
|
236
|
-
startedAt: Date.now(),
|
|
255
|
+
status: queued.kind === "runtime-feedback-replay" ? "waiting_executor" : "running",
|
|
256
|
+
...queued.kind === "runtime-feedback-replay" ? {} : { startedAt: Date.now() },
|
|
237
257
|
updatedAt: Date.now()
|
|
238
258
|
};
|
|
239
259
|
if (!await import_redis.mcpStorage.compare(jobKey, queued, started, import_mcp.MCP_JOB_TTL))
|
|
240
260
|
continue;
|
|
241
|
-
if (started.packageManifestSha256 !== manifestHash) {
|
|
261
|
+
if (started.kind !== "runtime-feedback-replay" && started.packageManifestSha256 !== manifestHash) {
|
|
242
262
|
await import_redis.mcpStorage.compare(
|
|
243
263
|
jobKey,
|
|
244
264
|
started,
|
|
@@ -326,7 +346,8 @@ var main = async (options = {}) => {
|
|
|
326
346
|
});
|
|
327
347
|
const currentChild = child;
|
|
328
348
|
let timedOut = false;
|
|
329
|
-
|
|
349
|
+
let replayStartedAt;
|
|
350
|
+
const timeout = started.kind === "runtime-feedback-replay" ? void 0 : setTimeout(() => {
|
|
330
351
|
timedOut = true;
|
|
331
352
|
signalChild("SIGTERM");
|
|
332
353
|
}, JOB_TIMEOUT_MS);
|
|
@@ -346,11 +367,38 @@ var main = async (options = {}) => {
|
|
|
346
367
|
killAt ??= Date.now();
|
|
347
368
|
signalChild(Date.now() - killAt > 8e3 ? "SIGKILL" : "SIGTERM");
|
|
348
369
|
}
|
|
370
|
+
let replayStatus;
|
|
371
|
+
if (latest?.kind === "runtime-feedback-replay") {
|
|
372
|
+
try {
|
|
373
|
+
const root = process.env.MCP_REPLAY_REQUEST_ROOT || `${process.env.PROJECT_CWD || process.cwd()}/data/mcp-replay-requests`;
|
|
374
|
+
const state = JSON.parse(
|
|
375
|
+
await (0, import_promises2.readFile)(`${root}/${latest.id}.state`, "utf8")
|
|
376
|
+
);
|
|
377
|
+
if (state && typeof state === "object" && "status" in state && (state.status === "waiting_resources" || state.status === "waiting_executor" || state.status === "running"))
|
|
378
|
+
replayStatus = state.status;
|
|
379
|
+
} catch {
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (replayStatus === "running") {
|
|
383
|
+
replayStartedAt ??= latest?.startedAt ?? Date.now();
|
|
384
|
+
if (Date.now() - replayStartedAt > JOB_TIMEOUT_MS) {
|
|
385
|
+
timedOut = true;
|
|
386
|
+
signalChild("SIGTERM");
|
|
387
|
+
}
|
|
388
|
+
}
|
|
349
389
|
if (latest && !leaseLost)
|
|
350
390
|
await import_redis.mcpStorage.compare(
|
|
351
391
|
jobKey,
|
|
352
392
|
latest,
|
|
353
|
-
{
|
|
393
|
+
{
|
|
394
|
+
...latest,
|
|
395
|
+
...replayStatus && !latest.cancelRequested ? {
|
|
396
|
+
status: replayStatus,
|
|
397
|
+
...replayStatus === "running" && !latest.startedAt ? { startedAt: Date.now() } : {}
|
|
398
|
+
} : {},
|
|
399
|
+
logs: [...logs],
|
|
400
|
+
updatedAt: Date.now()
|
|
401
|
+
},
|
|
354
402
|
import_mcp.MCP_JOB_TTL
|
|
355
403
|
);
|
|
356
404
|
} catch {
|