@tradejs/cli 3.1.29-beta.285 → 3.1.29-beta.287

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.
@@ -62,14 +62,16 @@ const timer = setInterval(() => {
62
62
  if (result.exitCode !== 0) {
63
63
  let fd;
64
64
  try {
65
- fd = fs.openSync(file + '.log', fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
65
+ const logId = result.logRequestId === undefined ? id : result.logRequestId;
66
+ if (typeof logId !== 'string' || !/^[a-f0-9]{64}$/.test(logId)) throw new Error('Invalid replay log identity');
67
+ fd = fs.openSync(path.join(root, logId) + '.log', fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
66
68
  const size = fs.fstatSync(fd).size;
67
69
  const tail = Buffer.alloc(Math.min(size, 65536));
68
70
  fs.readSync(fd, tail, 0, tail.length, Math.max(0, size - tail.length));
69
71
  // The worker redacts each line before persisting or returning it through MCP.
70
72
  for (const line of tail.toString('utf8').split(/[\\r\\n]+/).filter(Boolean).slice(-150)) console.error('Host replay: ' + line);
71
73
  } catch (error) {
72
- console.error('Host replay log unavailable: ' + error.code);
74
+ console.error('Host replay: ' + result.message + '; log unavailable: ' + (error.code || error.message));
73
75
  } finally { if (fd !== undefined) fs.closeSync(fd); }
74
76
  }
75
77
  clearInterval(timer);
@@ -2364,6 +2364,10 @@ var validateStrategyResearchSpec = (value) => {
2364
2364
  };
2365
2365
  if (!spec.budget || !spec.selection || !spec.diagnostics)
2366
2366
  throw new Error("Missing research budgets or criteria");
2367
+ if (spec.featurePaths !== void 0 && (!Array.isArray(spec.featurePaths) || !spec.featurePaths.length || spec.featurePaths.length > 64 || new Set(spec.featurePaths).size !== spec.featurePaths.length || spec.featurePaths.some(
2368
+ (path13) => typeof path13 !== "string" || !/^additionalIndicators\.[a-zA-Z0-9_.]+$/.test(path13) || classifyAiPocketFeaturePath(path13.split(".")) !== "eligible"
2369
+ )))
2370
+ throw new Error("Feature paths must be unique causal market features");
2367
2371
  positive(spec.budget.maxVariants, "maxVariants", 12);
2368
2372
  if (!Number.isInteger(spec.minQuality) || spec.minQuality < 1 || spec.minQuality > 5)
2369
2373
  throw new Error("minQuality must be 1 through 5");
@@ -4392,6 +4396,7 @@ var runStrategyResearch = async (parameters) => {
4392
4396
  parentSpec.selection,
4393
4397
  parentSpec.portfolio,
4394
4398
  parentSpec.minQuality,
4399
+ parentSpec.featurePaths,
4395
4400
  parentSpec.diagnostics
4396
4401
  ]) !== sha256Json([
4397
4402
  spec.core.variants.map((item) => item.resolvedConfig),
@@ -4400,6 +4405,7 @@ var runStrategyResearch = async (parameters) => {
4400
4405
  spec.selection,
4401
4406
  spec.portfolio,
4402
4407
  spec.minQuality,
4408
+ spec.featurePaths,
4403
4409
  spec.diagnostics
4404
4410
  ]) || parentState.lineage.runtimeSha256 !== state.lineage.runtimeSha256 || parentState.lineage.contextEnvironmentSha256 !== state.lineage.contextEnvironmentSha256)
4405
4411
  throw new Error(
@@ -5779,9 +5785,10 @@ var createNativeStrategyResearchBackend = (parameters) => {
5779
5785
  throw new Error(
5780
5786
  "Tester workers are already active; research will resume when the host is free"
5781
5787
  );
5782
- if (import_node_os2.default.freemem() < spec.budget.minimumFreeMemoryMb * 1024 * 1024)
5788
+ const availableBytes = import_node_os2.default.platform() === "darwin" ? process.availableMemory() : import_node_os2.default.freemem();
5789
+ if (!Number.isFinite(availableBytes) || availableBytes < spec.budget.minimumFreeMemoryMb * 1024 * 1024)
5783
5790
  throw new Error(
5784
- "Insufficient free host memory for the frozen research budget"
5791
+ "Insufficient available host memory for the frozen research budget"
5785
5792
  );
5786
5793
  };
5787
5794
  return {
@@ -6234,9 +6241,14 @@ var createNativeStrategyResearchBackend = (parameters) => {
6234
6241
  throw new Error(
6235
6242
  `Native/export reconciliation is ${reconciliation.status}: ${reconciliation.reasons.join("; ")}`
6236
6243
  );
6244
+ const researchSignalId = (symbol, signalId) => JSON.stringify([symbol, signalId]);
6237
6245
  const trades = loaded.trades.filter(
6238
6246
  (trade) => trade.signalTimestamp >= spec.core.window.start && trade.signalTimestamp < spec.core.window.end && trade.exitTimestamp < spec.core.window.end
6239
- );
6247
+ ).map((trade) => ({
6248
+ ...trade,
6249
+ // Strategy-generated signal IDs need not be unique across symbols.
6250
+ signalId: researchSignalId(trade.symbol, trade.signalId)
6251
+ }));
6240
6252
  const tradeBySignal = new Map(
6241
6253
  trades.map((trade) => [trade.signalId, trade])
6242
6254
  );
@@ -6260,9 +6272,10 @@ var createNativeStrategyResearchBackend = (parameters) => {
6260
6272
  for await (const line of lines) {
6261
6273
  if (!line.trim()) continue;
6262
6274
  const row = JSON.parse(line);
6263
- const trade = tradeBySignal.get(row.signalId);
6264
- if (!trade || seen.has(row.signalId)) continue;
6265
- seen.add(row.signalId);
6275
+ const signalId = researchSignalId(row.symbol, row.signalId);
6276
+ const trade = tradeBySignal.get(signalId);
6277
+ if (!trade || seen.has(signalId)) continue;
6278
+ seen.add(signalId);
6266
6279
  if (discoveryOnly && (row.timestamp >= spec.testSince || trade.exitTimestamp >= spec.testSince)) {
6267
6280
  if (row.timestamp < spec.testSince) purgedLabels++;
6268
6281
  continue;
@@ -6300,7 +6313,7 @@ var createNativeStrategyResearchBackend = (parameters) => {
6300
6313
  featureProfile: "all"
6301
6314
  });
6302
6315
  for (const key of Object.keys(features))
6303
- if (!key.startsWith("additionalIndicators.") || features[key] == null)
6316
+ if (!key.startsWith("additionalIndicators.") || features[key] == null || spec.featurePaths && !spec.featurePaths.includes(key))
6304
6317
  delete features[key];
6305
6318
  featureValues += Object.keys(features).length;
6306
6319
  if (featureValues > 5e6)
@@ -6319,7 +6332,7 @@ var createNativeStrategyResearchBackend = (parameters) => {
6319
6332
  pathCoverage[key]++;
6320
6333
  }
6321
6334
  rows.push({
6322
- signalId: row.signalId,
6335
+ signalId,
6323
6336
  symbol: row.symbol,
6324
6337
  strategy: row.strategyName,
6325
6338
  timestamp: row.timestamp,
@@ -6334,7 +6347,7 @@ var createNativeStrategyResearchBackend = (parameters) => {
6334
6347
  });
6335
6348
  if (discoveryOnly) {
6336
6349
  eventInputs.push({
6337
- signalId: row.signalId,
6350
+ signalId,
6338
6351
  timestamp: row.timestamp,
6339
6352
  symbol: row.symbol,
6340
6353
  direction: row.direction,
@@ -6345,8 +6358,8 @@ var createNativeStrategyResearchBackend = (parameters) => {
6345
6358
  });
6346
6359
  const key = `${row.direction}:${trade.netProfit > 0}`;
6347
6360
  const bucket = sampleBuckets.get(key) ?? [];
6348
- bucket.push(row.signalId);
6349
- sampleInputs.set(row.signalId, row);
6361
+ bucket.push(signalId);
6362
+ sampleInputs.set(signalId, row);
6350
6363
  bucket.sort((a, b) => sha256Json(a).localeCompare(sha256Json(b)));
6351
6364
  while (bucket.length > spec.diagnostics.samplePerSideAndOutcome)
6352
6365
  sampleInputs.delete(bucket.pop());
@@ -69,14 +69,16 @@ const timer = setInterval(() => {
69
69
  if (result.exitCode !== 0) {
70
70
  let fd;
71
71
  try {
72
- fd = fs.openSync(file + '.log', fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
72
+ const logId = result.logRequestId === undefined ? id : result.logRequestId;
73
+ if (typeof logId !== 'string' || !/^[a-f0-9]{64}$/.test(logId)) throw new Error('Invalid replay log identity');
74
+ fd = fs.openSync(path.join(root, logId) + '.log', fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
73
75
  const size = fs.fstatSync(fd).size;
74
76
  const tail = Buffer.alloc(Math.min(size, 65536));
75
77
  fs.readSync(fd, tail, 0, tail.length, Math.max(0, size - tail.length));
76
78
  // The worker redacts each line before persisting or returning it through MCP.
77
79
  for (const line of tail.toString('utf8').split(/[\\r\\n]+/).filter(Boolean).slice(-150)) console.error('Host replay: ' + line);
78
80
  } catch (error) {
79
- console.error('Host replay log unavailable: ' + error.code);
81
+ console.error('Host replay: ' + result.message + '; log unavailable: ' + (error.code || error.message));
80
82
  } finally { if (fd !== undefined) fs.closeSync(fd); }
81
83
  }
82
84
  clearInterval(timer);